构造方法调用
在 Java 中,一个类的构造方法可以调用同一个类的另一个构造方法,这通常被称为显式构造方法调用或构造器链。

语法
使用 this 关键字来实现。this 在这里代表“当前类的实例”。
语法有两种形式:
this(参数列表);this(参数列表);
这两种形式在功能上是完全相同的,都是调用当前类的另一个构造方法,我们使用第一种形式。
规则与限制
使用构造方法调用时,必须遵守以下非常重要的规则:

-
必须是第一条语句:在一个构造方法内部,对另一个构造方法的调用必须是该方法的第一条可执行语句,这意味着在
this(...)之前,不能有任何其他代码(比如变量初始化、System.out.println()等)。 -
不能形成循环:不能出现构造方法 A 调用 B,B 又调用 A 的死循环情况。
-
只能调用一个:在一个构造方法中,只能调用一个其他的构造方法,不能同时调用多个,
this(...); this(...);是错误的。 -
与
super(...)互斥:构造方法调用(this(...))和父类构造方法调用(super(...))不能同时出现在同一个构造方法中,因为两者都必须是第一条语句,所以只能选择其一,如果一个构造方法既没有this(...)也没有super(...),Java 编译器会自动在第一行插入一个对父类无参构造方法super()的调用。
(图片来源网络,侵删)
为什么需要构造方法调用?
-
避免代码重复:这是最主要的原因,假设你的类有多个构造方法,它们都需要执行一段相同的初始化代码(比如给某些成员变量赋值),你可以将这段公共的初始化代码放在一个主构造方法中,然后让其他构造方法通过
this(...)来调用它,从而避免重复编写。 -
保证初始化逻辑的一致性:通过集中管理初始化逻辑,可以确保所有对象都按照相同的方式被创建和初始化,减少出错的可能性。
代码示例
下面通过一个 Person 类的例子来演示构造方法调用的用法。
示例 1:基本用法(避免代码重复)
假设 Person 类总是需要 name,但 age 和 address 是可选的。
public class Person {
private String name;
private int age;
private String address;
// 构造方法 3:提供所有信息(最完整的构造方法)
public Person(String name, int age, String address) {
// 1. 首先调用 "主构造方法" 来初始化 name
this(name);
// 2. 然后执行自己的特有逻辑
this.age = age;
this.address = address;
System.out.println("调用全参数构造方法");
}
// 构造方法 2:提供 name 和 age
public Person(String name, int age) {
// 1. 调用 "主构造方法" 来初始化 name
this(name);
// 2. 然后执行自己的特有逻辑
this.age = age;
System.out.println("调用 name 和 age 构造方法");
}
// 构造方法 1:只提供 name(被其他构造方法调用的 "主构造方法")
public Person(String name) {
// 这是第一条语句,没有调用其他构造方法
this.name = name;
System.out.println("调用 name 构造方法(基础初始化)");
}
public void displayInfo() {
System.out.println("Name: " + name + ", Age: " + age + ", Address: " + (address != null ? address : "N/A"));
}
public static void main(String[] args) {
System.out.println("--- 创建 person1 ---");
Person person1 = new Person("Alice", 30, "New York");
// 输出:
// --- 创建 person1 ---
// 调用 name 构造方法(基础初始化)
// 调用全参数构造方法
System.out.println("\n--- 创建 person2 ---");
Person person2 = new Person("Bob", 25);
// 输出:
// --- 创建 person2 ---
// 调用 name 构造方法(基础初始化)
// 调用 name 和 age 构造方法
System.out.println("\n--- 创建 person3 ---");
Person person3 = new Person("Charlie");
// 输出:
// --- 创建 person3 ---
// 调用 name 构造方法(基础初始化)
System.out.println("\n--- 显示信息 ---");
person1.displayInfo(); // Name: Alice, Age: 30, Address: New York
person2.displayInfo(); // Name: Bob, Age: 25, Address: N/A
person3.displayInfo(); // Name: Charlie, Age: 0, Address: N/A
}
}
分析:
Person(String name)是最基础的构造方法,它负责最核心的初始化——设置name。Person(String name, int age)想要复用设置name的逻辑,所以它第一行就是this(name),然后才设置age。Person(String name, int age, String address)也是同理,它首先调用this(name, age)来复用设置name和age的逻辑,然后再设置address。- 这样,
name的赋值逻辑只在一个地方(Person(String name))存在,符合 DRY (Don't Repeat Yourself) 原则。
错误示例
错误 1:this(...) 不是第一条语句
public class BadExample {
private int x;
private int y;
public BadExample(int x, int y) {
// 错误!在 this(...) 之前有代码
this.x = x;
this(y); // 编译错误:对构造方法的调用必须是第一条语句
}
public BadExample(int y) {
this.y = y;
}
}
错误 2:与 super(...) 同时使用
public class BadExample2 extends Parent {
private int z;
// 错误!不能同时使用 this(...) 和 super(...)
public BadExample2(int x, int y, int z) {
super(x); // 编译错误:对父类构造方法的调用必须是第一条语句
this(z); // 编译错误:对构造方法的调用必须是第一条语句
}
}
class Parent {
public Parent(int x) {
// ...
}
}
与 super(...) 的关系
构造方法调用 this(...) 和父类构造方法调用 super(...) 是 Java 继承体系中对象初始化的关键部分。
初始化顺序:
当一个 new Child() 执行时,会发生以下事情:
- 分配内存空间给
Child对象。 - 初始化阶段开始(从父类到子类):
- 首先调用
Parent类的构造方法来初始化Parent部分的成员。 - 在
Parent的构造方法中,如果没有显式调用super(...)或this(...),则默认调用super()(即Object的无参构造方法)。 - 然后初始化
Parent的成员变量。 - 最后执行
Parent构造方法的代码体。 - 调用
Child类的构造方法来初始化Child部分的成员。 - 在
Child的构造方法中,如果没有显式调用super(...)或this(...),则默认调用super()(即Parent的无参构造方法)。 - 然后初始化
Child的成员变量。 - 最后执行
Child构造方法的代码体。
- 首先调用
this(...) 和 super(...) 的选择:
在一个子类的构造方法中,你必须决定如何初始化父类部分:
- 使用
super(...):明确告诉 Java 用父类的哪个构造方法来初始化父类部分,这是最常见和推荐的做法,因为它能精确控制初始化过程。 - 使用
this(...):调用子类的另一个构造方法,如果被调用的那个子类构造方法中又有super(...),那么最终还是会调用到父类的构造方法。 - 两者都不用:编译器会自动在第一行插入
super(),调用父类的无参构造方法。如果父类没有无参构造方法,这会导致编译错误!
| 特性 | 描述 |
|---|---|
| 语法 | this(参数列表); |
| 核心规则 | 必须是构造方法中的第一条语句。 |
| 目的 | 代码复用,避免在多个构造方法中重复编写相同的初始化代码。 |
与 super 的关系 |
this(...) 和 super(...) 互斥,两者都只能出现在构造方法的第一行。 |
| 最佳实践 | 通常将参数最多、功能最全的构造方法作为“主构造方法”,其他构造方法通过 this(...) 调用它,对于继承,确保子类能正确调用到父类的构造方法以完成父类部分的初始化。 |
掌握构造方法调用是编写健壮、可维护 Java 代码的重要一环。
