虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型,这就是虚拟机的类加载机制。
虚拟机规范规定了有且仅有四种情况必须立即对类进行初始化:
(1)遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。
(2)使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发初始化。
(3)当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。
(4)当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类
上面的情况称为对一个类进行主动引用。除此之外所有引用类的方式,都不会触发初始化,称为被动引用。
被动引用情形一:
package com.java.initialization;public class SuperClass { static { System.out.println("SuperClass init"); } public static int value = 123;}
package com.java.initialization;public class SubClass extends SuperClass { static { System.out.println("SubClass init"); }}
package com.java.initialization;public class NotInitialization { /** * @param args */ public static void main(String[] args) { System.out.println(SubClass.value); }}结果:
SuperClass init123
这个很奇怪吧!只输出了SuperClass init ,对于静态字段,只有直接定义这个字段的类才会被初始化,因此通过其子类来引用父类中定义的静态字段,只会触发父类的初始化而不会触发子类的初始化。
被动引用情景二
package com.java.initialization;public class ConstClass { static { System.out.println("ConstClass inti "); } public static final String name = "name";}package com.java.initialization;public class NotInitialization { /** * @param args */ public static void main(String[] args) { System.out.println(ConstClass.name); }}结果:
name
这有很奇怪了!没有输出“ConstClass inti ”,这是因为constClass类中的常量name,在编译阶段将此常量的值存储到了NotInitialization类的常量池中,实际上NotInitialization的class文件之中没有constClass类的符号引用入口,者各类在编译成class之后就不存在任何联系了。
注:当一个类在初始化时,要求其父类全部都已经初始化过了,但是一个接口在初始化,并不要求其父类接口全部都完成了初始化,只有在真正使用到父接口的时候才会初始化。
被动引用情景三:通过数组定义类应用类:ClassA [] array=new ClassA[10]。触发了一个名为[LClassA的类的初始化,它是一个由虚拟机自动生成的、直接继承于Object的类,创建动作由字节码指令newarray触发。
参考《深入理解java虚拟机》