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虚拟机一书。