类的生命周期:
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:
加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中验证、准备、解析3个部分统称为连接(Linking)
解析阶段的特殊性:
加载、验证、准备、初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班地开始,而解析阶段则不一定:
它在某些情况下可以在初始化阶段之后再开始,这是为了支持Java语言的运行时绑定(也称为动态绑定或晚期绑定)。注意,这里笔者写的是按部就班地“开始”,而不是按部就班地“进行”或“完成”,强调这点 是因为这些阶段通常都是互相交叉地混合式进行的,通常会在一个阶段执行的过程中调用激活另外一个阶段。
类初始化的5种情况:
几个被动引用的例子:
第一个例子:
对于静态字段,只有直接定义这个字段的类才会被初始化,因此通过其子类来引用父类中定义的静态字段,只会触发父类的初始化而不会触发子类的初始化。至于是否要触发子类的加载和验证,在虚拟机规范中并未明确规定,这点取决于虚拟机的具体实现。对于SunHotspot虚拟机来说,可通过-XX:+TraceClassLoading参数观察类加载。
第二个例子:
第三个例子:
上述代码运行之后,也没有输出“ConstClass init!”,这是因为虽然在Java源码中引用了ConstClass类中的常量HELLOWORED,但其实在编译阶段通过常量传播优化,已经将此常量的值“helloworld”存储到了NotInitialization类的常量池中,以后NotInitialization对常量ConstClass.HEELOWORLE的引用实际都被转化为NotInitialization类对自身常量池的引用了。也就是说,实际上Notlnitialization的Class文件之中并没有ConstClass类的符号引用入口,这两个类在编译成Class之后就不存在任何联系了。
接口初始化的特殊性:
与接口初始化规则的区别只有第三条:
当一个类在初始化时,要求其父类全部都已经初始化过了,但是一个接口在初始化时,并不要求其父接口全部都完成了初始化,只有在真正使用到父接口的时候(如引用接口中定义的常量)才会初始化。