Java类加载机制:加载的时机

    Java类的加载机制与C语言不同。C语言在在编译器进行连接工作,而在Java中,类的加载、连接和初始化工作都是在程序运行期间完成的。这种策略虽然在类加载时稍微增加了性能开销,却未Java程序提供了高度的灵活性。Java天生可以动态扩展的语言特性就得益于这一点。

1.类的生命周期

   类的生命周期包括:加载、验证、准备、解析、初始化、使用和卸载。其中验证、准备、解析三个部分统称连接(Linking)。7个阶段的发生顺序如图。


    图中的加载、验证、准备、初始化、卸载这5个阶段的顺序是固定的,解析阶段则不一定:解析阶段可以在初始化之后才开始,以支持Java的动态绑定特性。这里说的顺序固定是说阶段开始的先后,不是像排队一样的一个完成再到另一个。通常这些阶段都是互相交叉进行的,会在一个阶段执行的过程中调用其他阶段。

2.类加载的时机

    我们编写的代码存储在java文件中,java源代码通过编译生成Java虚拟机可识别的字节码,存储在Class文件中。运行java程序时需要将Class文件中的信息加载到Java虚拟机中,而这个过程就是类加载的过程。

    那么什么时候需要进行类的加载过程呢?Java规范中没有强制约束“加载”的时机,但是对“初始化”的时间做了明确规定。要进行初始化,自然要先完成加载、验证和准备。有且只有5种情况下必须立刻对类进行初始化。

1.遇到new、getstatic、putstatic、invokestatic这4条字节码指令时,如果类没有进行初始化,要先初始化。最典型的场景就是:

    使用new关键字实例化对象时

    读取或设置一个类的静态字段(被final修饰、在编译器把结果放入常量池的静态字段除外)时

    调用一个类的静态方法时

2.使用java.lang.reflect包对类进行反射调用时,如果类没有进行过初始化,要先进行初始化。

3.初始化一个类A,发现A的父类B还没有初始化,要先触发B的初始化。

4.虚拟机启动时,用户要指定一个主类,虚拟机会先初始化这个主类。

5.当使用JDK1.7的动态语言支持时,如果一个Java.lang.invoke.MethodHandle实例最后的解析结果是REF_getStatic、REF_outStatic、REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。

    以上触发初始化的条件都包括该类未经过初始化,如果已经初始化,直接用就可以。

    以上五种情况属于对类进行“主动引用”,除此以外,所有引用类的方式都不会触发初始化,称为“被动引用”。

4.接口与类在加载过程上的区别

    在加载时,接口与类的区别在于,接口在初始化时不要求其所有父接口全部完成初始化,只需要在真正用到到父接口时(如引用父接口中定义的常量)才需要初始化。

类加载的过程

1.加载

    在加载阶段,虚拟机要完成的工作有以下3件:

    (1)通过一个类的全限定名来获取定义此类

猜你喜欢

转载自blog.csdn.net/daniel_2046/article/details/80817710