本文参考《深入理解 JAVA 虚拟机》第 2 版,此书 JDK 版本为 1.7。
主动引用
java 类的初始化阶段,虚拟机规范严格规定了 5 种情况必须立即对类进行初始化。
- 遇到 new、getstatic、putstatic 或 invokestatic 这 4 条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。
- 使用 java.lang.reflect 包的方法对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化。
- 当初始化一个类时,如果发现其父类没有进行过初始化,则需要先触发其父类的初始化。
- 当虚拟机启动时,用户需要制定一个要执行的主类(包含 main()方法的那个类),虚拟机会先初始化这个类。
- 当使用 JDK1.7 的动态语言支持时,如果一个 java.lang.invoke.MethodHandle 实例最后的解析结果 REF_geStatic、REF_putStatic、REF_invokeStatic 的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
被动引用
除了上述 5 种场景,其他所有类的方式都不会触发初始化,称为被动引用。下面举 3 个例子来说明。
1、子类引用父类静态变量
public class SuperClass{
static {
System.out.println("SuperClass init!");
}
public static int value = 123;
}
public class SubClass extends SuperClass{
static {
System.out.println("SubClass init!");
}
}
public class InitTest{
public static void main(String[] args){
System.out.println(SubClass.value);
}
}
//执行结果
//SuperClass init!
//123
结论:对于静态变量,只有对应这个静态变量的类才会被初始化,通过子类调用只会使父类初始化,子类不会初始化。
2、创建对象数组
public class InitTest{
public static void main(String[] args){
SuperClass[] array = new SuperClass[10];
}
}
//运行后没有输出
结论:创建对象数组不会使对象的类初始化。注:会使数组类初始化,在这个例子中,数组类是"[L 包名.SuperClass"
3、调用类中的常量
public class ConstClass{
static {
System.out.println("ConstClass init!");
}
public static int value = 123;
}
public class InitTest{
public static void main(String[] args){
System.out.println(ConstClass.value);
}
}
//执行结果
//123
结论:调用对象常量不会触发类初始化。书中解释:在编译阶段通过常量传播优化,已经将此常量的值“123”存储到了 InitTest 类的常量池中,以后 InitTest 对常量 ConstClass.value 的引用实际都转换为 InitTest 对自身常量池的引用。
欢迎来到这里!
我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。
注册 关于