核心原则
在 Java 中,当一个对象被创建时(使用 new 关键字),其构造函数的调用遵循一个固定的顺序,这个顺序是由 Java 语言规范严格定义的,这个顺序可以总结为以下几步:

- 父类静态初始化块
- 子类静态初始化块
- 父类实例初始化块 / 父类成员变量初始化
- 父类构造函数
- 子类实例初始化块 / 子类成员变量初始化
- 子类构造函数
为了更好地理解,我们用一个流程图来表示:
graph TD
A[创建子类对象 new SubClass()] --> B[加载 SubClass 类和父类 SuperClass 类];
B --> C[执行 SuperClass 静态初始化块];
C --> D[执行 SubClass 静态初始化块];
D --> E[为 SuperClass 分配内存空间];
E --> F[执行 SuperClass 实例初始化块/成员变量初始化];
F --> G[调用 SuperClass 的构造函数];
G --> H[为 SubClass 分配内存空间];
H --> I[执行 SubClass 实例初始化块/成员变量初始化];
I --> J[调用 SubClass 的构造函数];
J --> K[对象创建完成];
详细步骤与代码示例
下面我们通过一个具体的代码示例来一步步追踪这个过程。
类的定义
我们定义一个父类 SuperClass 和一个子类 SubClass。
// 父类 SuperClass
class SuperClass {
// 父类静态成员变量
public static String staticParentField = "父类静态成员变量 - 初始化";
// 父类实例成员变量
public String instanceParentField = "父类实例成员变量 - 初始化";
// 父类静态初始化块
static {
System.out.println("(1) 父类静态初始化块执行");
}
// 父类实例初始化块
{
System.out.println("(4) 父类实例初始化块执行");
}
// 父类无参构造函数
public SuperClass() {
// 构造函数中的第一行代码,如果没有显式调用,默认是 super()
System.out.println("(5) 父类构造函数执行");
}
}
// 子类 SubClass
class SubClass extends SuperClass {
// 子类静态成员变量
public static String staticChildField = "子类静态成员变量 - 初始化";
// 子类实例成员变量
public String instanceChildField = "子类实例成员变量 - 初始化";
// 子类静态初始化块
static {
System.out.println("(2) 子类静态初始化块执行");
}
// 子类实例初始化块
{
System.out.println("(6) 子类实例初始化块执行");
}
// 子类构造函数
public SubClass() {
// super() 在这里被隐式调用
System.out.println("(7) 子类构造函数执行");
}
}
创建对象并观察输出
我们在 Main 类中创建一个 SubClass 的实例。

public class Main {
public static void main(String[] args) {
System.out.println("--- 开始创建 SubClass 对象 ---");
SubClass sub = new SubClass();
System.out.println("--- SubClass 对象创建完成 ---");
}
}
执行结果:
--- 开始创建 SubClass 对象 ---
(1) 父类静态初始化块执行
(2) 子类静态初始化块执行
(4) 父类实例初始化块执行
(5) 父类构造函数执行
(6) 子类实例初始化块执行
(7) 子类构造函数执行
--- SubClass 对象创建完成 ---
结果分析
让我们对照上面的核心原则来分析输出结果:
-
父类静态初始化块
(1)- 当
new SubClass()被执行时,JVM 首先需要加载SubClass类,由于SubClass继承自SuperClass,SuperClass类也会被加载。 - 类加载时,静态成员变量和静态初始化块会按照在代码中出现的顺序执行,父类的静态部分先被执行。
- 当
-
子类静态初始化块
(2)在父类静态部分执行完毕后,轮到子类的静态部分执行,同样,这是类加载过程的一部分。
-
父类实例初始化块
(4)和 父类构造函数(5)- 静态部分执行完毕后,真正的对象创建开始了。
new SubClass()会先为父类SuperClass的部分分配内存空间。- 分配完内存后,会执行实例初始化块和成员变量的初始化,它们在构造函数之前执行。
- 父类的构造函数
(5)被调用,这是最关键的一步:子类构造函数在执行自己的代码之前,必须先调用父类的构造函数,如果没有显式使用super(...),编译器会自动在子类构造函数的第一行插入一个super()调用。
-
子类实例初始化块
(6)和 子类构造函数(7)- 父类构造函数执行完毕后,对象创建流程返回到子类。
- 为子类
SubClass的部分分配内存空间。 - 执行子类的实例初始化块和成员变量的初始化。
- 执行子类自己的构造函数
(7)的主体部分。
重要规则与注意事项
super() 的调用
- 规则:子类构造函数的第一行代码必须是
super(...)(调用父类构造函数)或this(...)(调用本类其他构造函数),如果两者都没有,编译器会自动在第一行插入一个无参的super()。 - 原因:这确保了在创建子类对象时,其父类部分已经被正确地初始化,父类可能定义了子类所依赖的状态或方法,因此必须先初始化父类。
错误示例:
class SubClass extends SuperClass {
public SubClass() {
System.out.println("This is wrong!"); // 编译错误!
// 因为 super() 必须是第一行,而这里没有
}
}
this() 的调用
- 规则:
this(...)和super(...)不能同时出现在同一个构造函数中,因为它们都必须是构造函数的第一行。 - 目的:
this(...)用于在一个构造函数中调用同一个类的另一个构造函数,以实现代码复用(避免重复的初始化代码)。
正确示例:
class Car {
private String model;
private int year;
public Car() {
this("Default Model", 2025); // 调用另一个构造函数
System.out.println("Car with default model created.");
}
public Car(String model, int year) {
this.model = model;
this.year = year;
System.out.println("Car created: " + model + ", " + year);
}
}
静态部分 vs. 实例部分
- 静态初始化块:只在类第一次被加载到 JVM 时执行一次,无论你创建多少个该类的对象,它都只执行一次。
- 实例初始化块:在每次创建对象时都会执行,并且在构造函数之前执行,当初始化代码对于多个构造函数是通用的,但又不想写成一个单独的方法时,实例初始化块非常有用。
构造函数链
这个过程形成了一个“构造函数链”(Constructor Chaining),从最顶层的父类一直向下传递到最终的子类,这确保了对象被从上到下、完整地构建起来。
总结表格
| 调用顺序 | 组件 | 执行时机 | 执行次数 |
|---|---|---|---|
| 1 | 父类静态初始化块 | 类加载时 | 1 次 |
| 2 | 子类静态初始化块 | 类加载时 | 1 次 |
| 3 | 父类实例初始化块 / 成员变量初始化 | 创建对象时,父类构造函数前 | 每次创建对象 |
| 4 | 父类构造函数 | 创建对象时,子类构造函数前 | 每次创建对象 |
| 5 | 子类实例初始化块 / 成员变量初始化 | 创建对象时,子类构造函数前 | 每次创建对象 |
| 6 | 子类构造函数 | 创建对象时 | 每次创建对象 |
理解这个调用顺序对于理解 Java 对象的生命周期、继承机制以及调试复杂的初始化问题至关重要。
