各位大牛。
以前这一部分基础不太薄弱,有点儿疑问出现。
话说例如我们编写一个httpservlet程序,可以继承tomcat中提供的HttpServlet的类。
当我们编译的时候必须指定这个servlet-api.jar的类包才能通过编译。
那么照理来说,有关HttpServlet中的所有东西都应该已经包含在已经编译出来的.class文件中啊。
可是当我们真正去调用自己编写servlet时,都需要通过类加载器将自己曾经引用过的类包加载。
我的疑问是既然当初java程序编译的时候就已经使用过那些.class文件,那为什么等到运行的时候也必须要加载相关的类?相关的类为啥没有包含在当初的servlet中直接编译成.class的字节码?不知我的问题明白没— —

解决方案 »

  1.   

    java编译的时候会检查类中使用的api是否在classpath中,如果存在它才会编译java文件为。class文件,但不会将在classpath中依赖的类也编译到.class文件中,没有必要(classpath中的API已经编译为.class文件了),当我们运行自己的.class类时,jvm会将我们的类中依赖的类也加载到内存中来,来保证我们自己的类运行正常,如果在classpath中没有找到我们依赖的类,则会包异常。
      

  2.   


    那我产生了一个问题。加入当初编译时候引用的包跟后来运行时候通过包加载器加载的包中文件不同会怎么样。
    假如说一开始有一个基类a,有一个test方法,没有任何功能。创建一个子类去继承a,然后调用test方法。编译这些文件然,然后只保留子类生成的.class,然后修改基类让test方法随便输出一句话,再单独编译基类。然后将新的基类和原本的子类放在一起去执行子类。那么这时候会出现什么方法?我现在木有编译环境⋯⋯打算回家试试⋯⋯而且我的这种程序可能比较小我在想当程序像servlet那样复杂的话,倘若用旧版本的tomcat servlet api编译之后,能否放在新版本上执行?可能会出现问题啊⋯⋯
      

  3.   

    首先Lz我们来想这样一个问题,有两个类A,B,分别编译为A.class和B.class,我们假设B类用到A类,即B类里有A a = new A(),或者说B exntends A,总之就是B使用到了A,那么LZ觉得B.class文件里会不会把A.class文件的内容全部包含近来,如果是这样,那么B使用到的类很多,那么b.class文件是不是会很大?所以,显然采用这种方式来编译生成class文件是不可取的。只能在b.class文件中用个标识(这个标识就是所谓的符号引用)来表示引用到了A.class文件,然后JVM去加载A.class。
    所以,即使你编译好了你的servlet,但是这个servlet.class本身并没有包含HttpServlet.class的信息,只是有个标识去指向HttpServlet.class,让JVM去查找加载,所以如果不指定servlet-api.jar,JVM就没法加载HttpServlet.class,所以就会出错。
      

  4.   


    我刚刚试验了一下。引用了其他类对象的函数时只跟最新类装载器中装在的类有关看来编译时对其他类文件内容的验证只是为了检查编译错误我是这样试验的:
    首先有这样一个class:
    public class a{
    public void say(){
    System.out.println("old");
    }
    }
    还有一个测试class:
    public class test{
    public static void main(String [] args){
    a test = new a();
    test.say();
    }
    }
    这时候编译test.java,会自动将test.java和a.java编译出test.class和a.java
    测试程序,输出的"old"
    然后将a.class删掉,修改a.java内容为:
    public class a{
    public void say(){
    System.out.println("new");
    }
    }
    这时候单独编译a.java,产生a.class
    这时候再运行test,输出的就是"old"
    也就是说,编译时候要求a.java必须存在的原因只是为了进行编译检查?因为程序运行的结果只取决于最后一次的编译啊⋯⋯
      

  5.   

    主要是我觉得日后如果进行大项目有可能出现一个严重的问题。。就是旧版本jar中同名的方法在新版本jar中有不一样的功能那整个系统就会崩溃的⋯⋯
    呵呵。是不是我太浅显了通过我上面的例子我只觉得在编译的时候就需要引入jar包(包中没有.java,只有编译完的.class)只是为了进行编译检查,将错误的可能性降低而已— —。。(或许还有些优化啥的?这些就不清楚了⋯⋯)
      

  6.   

    要知道,java的编译,只是编译成伪代码,不是真正的系统执行指令。
    所以不会像C那样,当你编译链接完了以后,就把(外部)链接的执行指令复制到自己的可执行文件中
    所以你会发现,C用到的外部链接对象越多,可执行文件就会越大
      

  7.   

    恩。。感觉出来了java中会把每个类单独编译成.class文件。就如同在windows下的动态链接库一样。用的时候就调用相应库的内容。。呵呵
      

  8.   

    确实很像,你把dll删掉,程序也跑不起来,同样的,你把别的class删掉,引用到这些class的程序也跑不起来