杰瑞科技汇

Java构造方法如何调用构造方法?

构造方法调用

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

Java构造方法如何调用构造方法?-图1
(图片来源网络,侵删)

语法

使用 this 关键字来实现。this 在这里代表“当前类的实例”。

语法有两种形式:

  1. this(参数列表);
  2. this(参数列表);

这两种形式在功能上是完全相同的,都是调用当前类的另一个构造方法,我们使用第一种形式。

规则与限制

使用构造方法调用时,必须遵守以下非常重要的规则:

Java构造方法如何调用构造方法?-图2
(图片来源网络,侵删)
  1. 必须是第一条语句:在一个构造方法内部,对另一个构造方法的调用必须是该方法的第一条可执行语句,这意味着在 this(...) 之前,不能有任何其他代码(比如变量初始化、System.out.println() 等)。

  2. 不能形成循环:不能出现构造方法 A 调用 B,B 又调用 A 的死循环情况。

  3. 只能调用一个:在一个构造方法中,只能调用一个其他的构造方法,不能同时调用多个,this(...); this(...); 是错误的。

  4. super(...) 互斥:构造方法调用(this(...))和父类构造方法调用(super(...))不能同时出现在同一个构造方法中,因为两者都必须是第一条语句,所以只能选择其一,如果一个构造方法既没有 this(...) 也没有 super(...),Java 编译器会自动在第一行插入一个对父类无参构造方法 super() 的调用。

    Java构造方法如何调用构造方法?-图3
    (图片来源网络,侵删)

为什么需要构造方法调用?

  1. 避免代码重复:这是最主要的原因,假设你的类有多个构造方法,它们都需要执行一段相同的初始化代码(比如给某些成员变量赋值),你可以将这段公共的初始化代码放在一个主构造方法中,然后让其他构造方法通过 this(...) 来调用它,从而避免重复编写。

  2. 保证初始化逻辑的一致性:通过集中管理初始化逻辑,可以确保所有对象都按照相同的方式被创建和初始化,减少出错的可能性。


代码示例

下面通过一个 Person 类的例子来演示构造方法调用的用法。

示例 1:基本用法(避免代码重复)

假设 Person 类总是需要 name,但 ageaddress 是可选的。

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) 来复用设置 nameage 的逻辑,然后再设置 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() 执行时,会发生以下事情:

  1. 分配内存空间给 Child 对象。
  2. 初始化阶段开始(从父类到子类)
    • 首先调用 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 代码的重要一环。

分享:
扫描分享到社交APP
上一篇
下一篇