首先来看下面的代码输出的结果是什么?
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. 类加载阶段(静态初始化)
-
加载
Z类:发现Z继承自X,需先加载父类X。 -
加载
X类:- 执行
X的静态块:
→ 输出static x。
- 执行
-
继续加载
Z类:- 执行
Z的静态块:
→ 输出static z。
- 执行
-
此时
Y类尚未加载(因为仅在实例化时才会触发)。
2. 实例化 Z 对象(执行顺序)
2.1 父类 X 的初始化
-
执行
X的实例初始化块:
→ 输出xx。 -
初始化
X的实例变量y = new Y():- 首次使用
Y类,触发Y的加载和静态初始化:
→ 输出static y。 - 调用
Y的构造函数:
→ 输出Y。
- 首次使用
-
执行
X的构造函数:
→ 输出X。
2.2 子类 Z 的初始化
-
初始化
Z的实例变量y = new Y():Y类已加载,直接调用构造函数:
→ 输出Y。
-
执行
Z的实例初始化块:
→ 输出zz。 -
执行
Z的构造函数:
→ 输出Z
static x
static z
xx
static y
Y
X
Y
zz
Z
关键规则总结
-
类加载顺序:父类优先于子类,静态块按代码顺序执行。
-
实例初始化顺序:
- 父类实例化 → 子类实例化。
- 实例初始化块和变量赋值和构造函数按代码顺序执行,
-
延迟加载:类在首次主动使用时(如
new、访问静态成员)才会加载并初始化。