一道面试题看java对象初始化的顺序

73 阅读1分钟

首先来看下面的代码输出的结果是什么?

public class X {
    {
        System.out.println("xx");
    }
    Y y = new Y();

    static {
        System.out.println( "static x");
    }
    public X(){
        System.out.println("X");
    }
}

public class Y {
    static {
        System.out.println( "static y");
    }
    public Y(){
        System.out.println("Y");
    }
}

public class Z extends X{
    Y y = new Y();
    {
        System.out.println("zz");
    }
    static {
        System.out.println( "static z");
    }
     public Z(){
         System.out.println("Z");
     }
}
public static void main(String[] args) {
    new Z();
}

详细分析

1. 类加载阶段(静态初始化)

  1. 加载 Z 类:发现 Z 继承自 X,需先加载父类 X

  2. 加载 X 类

    • 执行 X 的静态块:
      → 输出 static x
  3. 继续加载 Z 类

    • 执行 Z 的静态块:
      → 输出 static z
  4. 此时 Y 类尚未加载(因为仅在实例化时才会触发)。


2. 实例化 Z 对象(执行顺序)

2.1 父类 X 的初始化

  1. 执行 X 的实例初始化块
    → 输出 xx

  2. 初始化 X 的实例变量 y = new Y()

    • 首次使用 Y 类,触发 Y 的加载和静态初始化:
      → 输出 static y
    • 调用 Y 的构造函数:
      → 输出 Y
  3. 执行 X 的构造函数
    → 输出 X

2.2 子类 Z 的初始化

  1. 初始化 Z 的实例变量 y = new Y()

    • Y 类已加载,直接调用构造函数:
      → 输出 Y
  2. 执行 Z 的实例初始化块
    → 输出 zz

  3. 执行 Z 的构造函数
    → 输出 Z

static x
static z
xx
static y
Y
X
Y
zz
Z

关键规则总结

  1. 类加载顺序:父类优先于子类,静态块按代码顺序执行。

  2. 实例初始化顺序

    • 父类实例化 → 子类实例化。
    • 实例初始化块和变量赋值和构造函数按代码顺序执行,
  3. 延迟加载:类在首次主动使用时(如 new、访问静态成员)才会加载并初始化。