参考书籍:《深入理解Java虚拟机》周志明
1,类的生命周期
2,类的初始化
《Java虚拟机规范》严格规定有且仅有六种情况必须对类立即进行初始化,分别是:
1. 遇到new,getstatic,putstatic,invokestatic这四个字节码指令时,如果类型没有进行初始化,则需要进行初始化。生成四条指令的代码场景:
1. 使用new关键字实例化对象时
2. 读取或设置一个类型的静态字段时(被final修饰,已在编译期把结果放入常量池的静态字段除外)
3. 调用一个类型的静态方法时
- 使用java.lang.reflect包的方法对类型进行反射调用的时候,若没有初始化,则需要先进行初始化
- 初始化类的时候,如果发现其父类没有进行初始化,则先触发父类进行初始化
- 当虚拟机启动时,用户需要指定一个要执行的主类(包含main方法的类),会先初始化主类
- 当使用jdk7新加入的动态语言支持时,如果一个java.lang.invoke.MethodHandle实例最后的解析结果为REF_getStatic,REF_putStatic,REF_invokeStatic,REF_newInvokeSpecial四种类型的方法句柄,若方法句柄没有初始化,则需要先初始化
- 当一个接口中定义了jdk8新加入的默认方法时,若有该接口的实现类进行了初始化,那么该接口要在其之前进行初始化
这六种情况被称为主动引用,除此之外,所有引用类型的方式都不会触发初始化,也称为被动引用。被动引用方式:
- 通过子类引用父类的静态变量,不会导致子类初始化
public class NotInitTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(Child.value);
}
}
class Parent{
static {
System.out.println("初始化Parent类");
}
public static int value = 11;
}
class Child extends Parent{
static {
System.out.println("初始化Child");
}
}
// 输出结果
初始化Parent类
11
对于静态字段,只有直接定义这个字段的类才会被初始化,因此通过子类引用父类定义的静态字段,只有父类会被初始化
- 通过数组定义来引用类,不会触发此类的初始化
public class NotInitTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
Father [] fathers = new Father[10];
}
}
class Father{
static {
System.out.println("初始化Father类");
}
public static int value = 11;
}
//输出结果 无
说明没有触发类Father的初始化
- 常量在编译阶段会存入调用类的常量池中,本质上没有直接引用到定义常量的类,因此不会触发定义常量的类的初始化
public class NotInitTest {
public static void main(String[] args) {
// TODO Auto-generated method stub
System.out.println(Const.PI);
}
}
class Const {
static {
System.out.println("初始化Const");
}
public static final double PI = 3.14;
}
// 输出结果
3.14
没有输出“初始化Const”,因此没有对类Const进行初始化
另补充一个知识点:在继承中代码的执行顺序为:
- 父类静态对象,父类静态代码块
- 子类静态对象,子类静态代码块
- 父类非静态对象,父类非静态代码块
- 父类构造函数
- 子类非静态对象,子类非静态代码块
- 子类构造函数