深入分析Java类初始化顺序

分析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对象,只触发父类初始化,不会触发子类初始化


你可能感兴趣的:(Java)