06-引用类初始化问题
1.概念
在类与类之间引用时,是否要对引用类进行初始化,在jvm规范中规定了以下五种情况需要进行初始化引用类
■ 遇到 new 、 getstatic、putstatic或者invokestatic 这四条指令,生成这四条指令常见的操作: 使用new关键字实例化对象、读取或者设置一个类的静态字段(被final修饰的常量已在编译器把结果存入了常量池的静态字段除外)的时候、调用一个类的静态方法;
■ 使用反射的对类进行调用的时候,如果没有初始化,需要先初始化引用类;
■ 当初始化一个类的时候,如果发现父类还没有进行初始化,那么需要进行父类的初始化操作;
■ 当jvm启动的时候,用户需要指定一个要执行的主类,jvm会先初始化该类;
■ 使用jdk7动态语言支持,如果一个java.lang.invoke.MethodHandle实例最后解析结果为REF_getstatic,REF_putstatic,REF_invokestatic的方法句柄,并且这个方法句柄所对应的类没有进行初始化,则需要先触发其初始化
以上行为成为对一个类进行主动引用,除此之外,所有应用类的方式都不会触发类的初始化过程,称为被动调用
2. 案例
2.1
对于静态字段,只有直接定义这个字段的类才会被初始化
package exception.test; public class SuperClass { static{ System.out.println("super class init ! "); } public static int value = 123 ; } package exception.test; public class SubClass extends SuperClass{ static{ System.out.println(" sub class init . "); } } package exception.test; public class ExcpetionDemo { public static void main(String[] args) { /*String str1 = new String("abc") ; //执行堆中的对象 String str2 = "abc" ; //执行栈中的一个常量 abc System.out.println(str1 == str2 ); */ System.out.println(SubClass.value); //对于静态字段,只有直接定义这个字段的类才会被初始化 } }
执行结果:
super class init ! 123
2.2
执行一下代码
package exception.test; public class Test { public static void main(String[] args) { SuperClass[] sca = new SuperClass[10] ; } }
无任何输出,没有任何对static的调用,不会初始化;
2.3
package exception.test; public class ConstantClass { static{ System.out.println(" ConstantClass init "); } public final static String value = "123" ; } package exception.test; public class CallConstant { public static void main(String[] args) { System.out.println(ConstantClass.value); } }
输出结果 只打印123
常量value在类编译的时候就加入了CallConstant类的常量池中, 执行 ConstantClass.value 实际上是CallConstant对自身常量池的调用。
这里涉及到的案例来源于深入理解java虚拟机一书。