核心概念
在 Java 中,一个类的构造方法可以调用同一个类中的另一个构造方法,这个过程被称为显式构造器调用(Explicit Constructor Invocation)。

为什么需要这个功能? 主要目的是为了代码复用和保证初始化逻辑的集中性,假设你有一个类,它的几个构造方法都需要执行一段相同的初始化代码(比如给某个成员变量赋一个默认值),你可以把这段公共代码放在一个“主构造方法”中,然后让其他构造方法都去调用它,从而避免代码重复。
语法
有两种语法形式:
-
this关键字调用:调用同一个类中的其他构造方法。- 语法:
this(参数列表);
- 语法:
-
super关键字调用:调用直接父类的构造方法。
(图片来源网络,侵删)- 语法:
super(参数列表);
- 语法:
本篇重点讲解 this 的用法。
this() 调用规则
使用 this() 调用其他构造方法时,必须遵守以下非常重要的规则:
- 必须是第一条语句:
this()语句必须是构造方法中的第一条可执行语句,在它之前,不能有任何其他代码(除了注释)。 - 不能形成闭环:不能出现两个或多个构造方法互相调用的情况,否则会导致栈溢出错误。
- 不能同时使用:在一个构造方法中,不能同时使用
this()和super(),因为它们都必须是第一条语句,所以无法共存。
代码示例
下面通过一个经典的 Person 类来演示 this() 的用法。
假设 Person 类有 name 和 age 两个属性,我们希望:
- 有一个无参构造方法,创建一个“匿名”且年龄为 0 的人。
- 有一个只传
name的构造方法,创建一个指定名字但年龄为 0 的人。 - 有一个同时传
name和age的构造方法。
不使用 this() 的写法(代码重复)
public class Person {
private String name;
private int age;
// 构造方法1:无参
public Person() {
this.name = "Unknown"; // 重复的赋值逻辑
this.age = 0;
System.out.println("Person() called: " + this);
}
// 构造方法2:只传 name
public Person(String name) {
this.name = name; // 重复的赋值逻辑
this.age = 0;
System.out.println("Person(String name) called: " + this);
}
// 构造方法3:传 name 和 age
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person(String name, int age) called: " + this);
}
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
}
可以看到,构造方法1和构造方法2中都有 this.age = 0; 这行重复的代码。
使用 this() 的写法(代码复用)
我们可以将公共的初始化逻辑(设置默认年龄)放在参数最全的构造方法中,然后让其他构造方法通过 this() 来调用它。
public class Person {
private String name;
private int age;
/**
* 构造方法3:最完整的构造方法
* 这个方法将作为其他构造方法的“基础”。
*/
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person(String name, int age) called: " + this);
}
/**
* 构造方法2:只传 name
* 它调用最全的构造方法,并传递默认的 age = 0。
* 注意:this(...) 必须是第一条语句!
*/
public Person(String name) {
this(name, 0); // 调用 public Person(String name, int age)
System.out.println("Person(String name) called: " + this);
}
/**
* 构造方法1:无参
* 它也调用最全的构造方法,传递默认的 name 和 age。
*/
public Person() {
this("Unknown", 0); // 调用 public Person(String name, int age)
System.out.println("Person() called: " + this);
}
@Override
public String toString() {
return "Person{" + "name='" + name + '\'' + ", age=" + age + '}';
}
// 测试代码
public static void main(String[] args) {
System.out.println("--- Creating a person with no args ---");
Person p1 = new Person();
System.out.println("\n--- Creating a person with name only ---");
Person p2 = new Person("Alice");
System.out.println("\n--- Creating a person with name and age ---");
Person p3 = new Person("Bob", 30);
}
}
运行结果:
--- Creating a person with no args ---
Person(String name, int age) called: Person{name='Unknown', age=0}
Person() called: Person{name='Unknown', age=0}
--- Creating a person with name only ---
Person(String name, int age) called: Person{name='Alice', age=0}
Person(String name) called: Person{name='Alice', age=0}
--- Creating a person with name and age ---
Person(String name, int age) called: Person{name='Bob', age=30}
从结果可以看出:
new Person()实际上先执行了this("Unknown", 0),完成了成员变量的初始化。new Person("Alice")实际上先执行了this("Alice", 0)。new Person("Bob", 30)直接执行了它自己。
这样,公共的初始化逻辑就只写了一遍,代码更简洁、更易于维护。
与 super() 的关系(重要)
当一个类没有显式地用 this() 调用自身的其他构造方法时,它的构造方法默认会调用父类的无参构造方法 super()。
下面的两段代码在效果上是完全相同的:
代码1(隐式调用 super())
public class Child extends Parent {
public Child() {
// 这里隐含了一句 super();
System.out.println("Child's constructor");
}
}
代码2(显式调用 super())
public class Child extends Parent {
public Child() {
super(); // 显式调用父类的无参构造方法
System.out.println("Child's constructor");
}
}
调用链:
当一个 new Child() 执行时,会发生以下事情:
Child的构造方法被调用。Child构造方法中的super()(无论显式还是隐式)被调用。Parent的构造方法被调用。Parent类没有父类,或者其构造方法也调用了super(),这个过程会一直持续到Object类的构造方法。- 然后从
Object开始,一路返回,依次执行Parent和Child构造方法中super()或this()之后的代码。
| 特性 | 描述 |
|---|---|
| 目的 | 代码复用,避免在多个构造方法中重复编写相同的初始化代码。 |
| 语法 | this(参数列表); |
| 关键规则 | this() 必须是构造方法中的第一条可执行语句。 |
与 super() 的关系 |
this() 和 super() 不能同时出现在一个构造方法中,因为它们都必须是第一条语句,如果构造方法中没有 this(),则默认有 super()。 |
| 最佳实践 | 通常将参数最多的构造方法作为“主构造方法”,其他构造方法都通过 this() 来调用它,并将默认值作为参数传递过去。 |
