分析Java类初始化顺序之前,先回顾下类加载的过程和时机
类加载的过程
- 加载:通过类名从class文件获取二进制流,生成Class对象(在方法区)
- 连接:包括验证、准备(类变量分配内存、赋初值)、解析(编译期确定静态类型)
- 初始化:初始化类
类加载的时机
主动引用
- 实例化对象 new
- 访问静态字段(final除外) get/putstatic
- 调用静态方法 invokestatic
- 反射
- 父类未初始化
- 主类 main()
被动引用
- 通过子类引用父类静态字段,只会触发父类初始化
- 通过数组定义来引用类
- final**常量优化**
- 接口的父接口未初始化则不用初始化父接口
类的初始化
1.引用静态字段
class A {
//被动引用 【静态final】
public static final String a1 = "1";
//主动引用 【静态final】
public static final String a2 = new String("2");
static {
System.out.println("A static");
}
public A(){
System.out.println("A init");
}
}
public static void main(String[] args) {
System.out.println(A.a1);
System.out.println(A.a2);
}
输出:
1
A static
2
final的字段经过常量传播优化,在编译阶段存入调用类的常量池中,与被引用类无关系
而a2是对象
引用普通静态字段会触发初始化,final可常量优化的不会
2.父类未初始化
class A {
//被动引用 【静态final】
public static final String a1 = "1";
//主动引用 【静态非final】
public static final String a2 = new String("2");
static {
System.out.println("A static");
}
public A(){
System.out.println("A init");
}
}
class B extends A{
public static final String b1 = "3";
public static final String b2 = new String("4");
static {
System.out.println("B static");
}
public B(){
System.out.println("B init");
}
}
public static void main(String[] args) {
B b = new B();
}
输出:
A static
B static
A init
B init
若父类未初始化先初始化父类
3.子类引用父类静态字段
public static void main(String[] args) {
System.out.println("子类引用父类静态final 可常量优化 字段");
System.out.println(B.a1);
System.out.println("子类引用父类静态final对象");
System.out.println(B.a2);
}
输出:
子类引用父类静态final 可常量优化 字段
1
子类引用父类静态final对象
A static
2
子类引用父类静态final对象,只触发父类初始化,不会触发子类初始化