杰瑞科技汇

子类如何调用父类构造函数?

核心要点

在 Java 中,子类必须调用其父类的构造函数,这个调用是隐式显式的,并且是构造子类对象过程中第一步必须完成的操作。

子类如何调用父类构造函数?-图1
(图片来源网络,侵删)

为什么必须调用父类构造函数?

一个子类对象不仅包含了子类自己定义的成员变量和方法,它也继承了父类的所有成员变量和方法,在创建子类对象时,系统首先需要确保父类的部分被正确地初始化,如果父类没有定义无参构造函数,而子类又没有显式调用父类的其他构造函数,编译器就会报错。

子类对象 = 父类部分 + 子类自己部分,初始化必须从最顶层的父类开始,自上而下进行。


如何调用?(两种方式)

隐式调用 (默认情况)

如果子类的构造函数没有使用 super 关键字显式地调用父类的某个构造函数,Java 编译器会自动在子类构造函数的第一行插入一条对父类无参构造函数的调用。

示例:

子类如何调用父类构造函数?-图2
(图片来源网络,侵删)
class Parent {
    // 父类有一个无参构造函数
    public Parent() {
        System.out.println("Parent 的无参构造函数被调用");
    }
}
class Child extends Parent {
    // 子类的构造函数
    public Child() {
        // 这里虽然没有写 super(),但编译器会自动加上 super();
        System.out.println("Child 的构造函数被调用");
    }
}
public class Main {
    public static void main(String[] args) {
        Child child = new Child();
    }
}

输出结果:

Parent 的无参构造函数被调用
Child 的构造函数被调用

过程分析:

  1. new Child() 执行,开始创建 Child 对象。
  2. Child 的构造函数 Child() 被调用。
  3. 在执行 Child() 构造函数体内的代码之前,编译器自动插入了对 super() 的调用。
  4. super() 会去执行 Parent 类的无参构造函数 Parent(),打印出 "Parent 的无参构造函数被调用"。
  5. 父类构造执行完毕后,返回到 Child 的构造函数,继续执行 System.out.println(...),打印出 "Child 的构造函数被调用"。
  6. Child 对象创建完成。

显式调用 (使用 super 关键字)

当父类没有提供无参构造函数,或者你希望在子类构造函数中调用父类的有参构造函数以传递参数进行特定初始化时,就必须使用 super 关键字进行显式调用。

语法规则: super(参数列表);

子类如何调用父类构造函数?-图3
(图片来源网络,侵删)

重要规则:

  • super(参数列表) 必须是子类构造函数中的第一条可执行语句,这确保了父类在任何子类代码执行之前就被初始化。
  • 一个构造函数中不能同时调用 this()super()

示例 1:调用父类的有参构造函数

class Parent {
    private String name;
    // 父类只有有参构造函数,没有无参构造函数
    public Parent(String name) {
        this.name = name;
        System.out.println("Parent 的有参构造函数被调用,name = " + name);
    }
}
class Child extends Parent {
    private int age;
    // 子类构造函数,必须显式调用父类的构造函数
    public Child(String name, int age) {
        // super() 必须是第一行!调用父类的有参构造函数
        super(name);
        this.age = age;
        System.out.println("Child 的有参构造函数被调用,age = " + age);
    }
}
public class Main {
    public static void main(String[] args) {
        Child child = new Child("张三", 25);
    }
}

输出结果:

Parent 的有参构造函数被调用,name = 张三
Child 的有参构造函数被调用,age = 25

过程分析:

  1. new Child("张三", 25) 执行。
  2. Child 的构造函数 Child(String name, int age) 被调用。
  3. 第一条语句 super(name) 执行,它调用 Parent 类的构造函数 Parent(String name),并传递 "张三"。
  4. 父类构造函数执行完毕后,返回到 Child 的构造函数,继续执行 this.age = age;System.out.println(...)

示例 2:父类没有无参构造函数时的错误情况

如果我们把上面的 Child 类构造函数改成不显式调用 super,会发生什么?

// ... Parent 类同上,只有 Parent(String name) ...
class Child extends Parent {
    public Child(String name, int age) {
        // 编译错误!
        // this.age = age; // 错误:对父类构造函数的调用必须是构造函数中的第一条语句
    }
}

编译错误:

error: no suitable constructor found in Parent
        super();
        ^
    constructor Parent.Parent(String) is not applicable
      (actual and formal argument lists differ in length)
    constructor Parent.Parent() is not applicable
      (cannot access constructor Parent.Parent())
      reason: no default constructor available in Parent

这个错误信息告诉我们:

  1. 编译器试图自动插入 super() 来调用父类的无参构造函数。
  2. 但是在 Parent 类中找不到无参构造函数 Parent()
  3. 编译失败。

super()this() 的区别

特性 super() this()
含义 调用父类的构造函数。 调用本类的另一个构造函数。
作用 初始化对象继承自父类的部分。 在一个构造函数中调用另一个构造函数,以避免代码重复。
位置 必须在子类构造函数的第一行 必须在构造函数的第一行
共存 不能与 this() 在同一个构造函数中同时使用。 不能与 super() 在同一个构造函数中同时使用。

  1. 必须调用:子类构造函数在执行自己代码前,必须先调用父类的某个构造函数。
  2. 隐式 vs 显式
    • 如果父类有无参构造函数,子类构造函数可以不写 super(),编译器会自动添加 super()
    • 如果父类没有无参构造函数,或者子类想调用父类的有参构造函数,则必须在子类构造函数的第一行显式地使用 super(参数列表)
  3. 第一原则super()this() 调用必须是构造函数中的第一条语句,这保证了初始化顺序的正确性(从父到子)。

理解并掌握父类构造函数的调用机制,是写出健壮、可维护的 Java 面向对象代码的基础。

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