一个类的整个生命周期包括:加载,验证,准备,解析,初始化,使用,卸载
这里主要介绍一下必须对类进行“初始化”的5种情况
1、遇到new,getstatic,putstatic,invokestatic这四条字节码指令时;
2、对类进行反射调用时,如果类没有进行过初始化,则需先触发其初始化;
3、当初始化一个类时,其父类还没初始化,则需先触发其父类初始化;(PS:当一个接口在初始化时,并不要求其父接口全部都已初始化,只有在真正使用到父接口的时候才会初始化)
4、当虚拟机启动时,会先初始化主类;
5、当使用JDK1.7的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果 REF_getStatic, REF_gpuStatic,REF_invokeStatic的方法句柄,并且这个方法句柄所对应的类没有进行过初始化,则需要先触发其初始化。
以上五种场景称为对类进行主动引用,除此之外,所有引用类的方式都不会触发初始化,称为被动引用
下面介绍几个被动引用的例子
1、通过子类引用父类的静态字段
class FatherClass{
static{
System.out.println("FatherClass init");
}
public static int value = 123;
}
class SonClass extends FatherClass{
static{
System.out.println("SonClass init");
}
}
public class Demo{
public static void main(String[] args){
System.out.println(SonClass.value);
}
}
运行结果
可以看到,并没有输出SonClass init,子类没有被初始化,对于静态字段,只有直接定义这个字段的类才会被初始化
2、通过数组定义来引用类
class FatherClass{
static{
System.out.println("FatherClass init");
}
public static int value = 123;
}
public class Demo{
public static void main(String[] args){
FatherClass[] ar = new FatherClass[10];
}
}
该程序不会有任何输出,没有输出FatherClass init,说明没有触发FatherClass初始化
3、调用常量时
class ConstClass{
static{
System.out.println("ConstClass init");
}
public static final String STR = "hello";
}
public class Demo{
public static void main(String[] args){
System.out.println(ConstClass.STR);
}
}
运行结果
从结果中可以看出,并没有出现ConstClass init,说明定义常量的类没有被初始化,常量在编译阶段会存入调用类的常量池中,本质上并没有直接引用到定义常量的类。