Java 初始化顺序极其微妙:**父类构造器 → 子类字段显式初始化/实例代码块 → 子类构造器**。若父类构造器调用了被子类覆盖的方法,且该方法访问子类字段,会导致字段仍为默认值(null/0),而非预期的显式初始化值。
class Base {
Base() { preProcess(); } // 构造器调用可覆盖方法
void preProcess() { }
}
class Derived extends Base {
public String whenAmISet = "set when declared"; // 显式初始化
void preProcess() { whenAmISet = "set in preProcess()"; } // 覆盖方法
}
public class StaticTest {
public static void main(String[] args) {
Derived d = new Derived();
System.out.println(d.whenAmISet); // 输出: set when declared
}
}
编译器将子类初始化逻辑插入构造器 <init> 中,**顺序为**:
super()(父类构造器)。等价代码:
class Derived extends Base {
public String whenAmISet;
{ whenAmISet = "set when declared"; } // 实例初始化块,在 super() 之后、构造器体之前
void preProcess() { whenAmISet = "set in preProcess(); }
}
结果:最终打印 `"set when declared"`,字段初始化「赢了」。
class Derived extends Base {
public String whenAmISet = "set when declared";
public Derived() { whenAmISet = "set in constructor"; } // 显式构造器
void preProcess() { whenAmISet = "set in preProcess()"; }
}
// 输出: set in constructor
super()结果:打印 `"set in constructor"`。
| 阶段 | 执行顺序 | 场景一结果 | 场景二结果 |
|---|---|---|---|
| 1. 父类构造器 | 最先 | 调用 preProcess() → set in preProcess | 调用 preProcess() → set in preProcess |
| 2. 子类字段显式初始化/实例块 | 第 2 | set when declared (最终) | set when declared |
| 3. 子类构造器体 | 最后 | 无 | set in constructor (最终) |
final、private 或 static。javac StaticTest.java
javap -c Derived.class # 查看 方法字节码指令顺序
通过字节码指令顺序(invokespecial super → putfield 字段初始化 → 构造器体代码)可一锤定音。