杰瑞科技汇

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

核心要点

  1. 隐式调用:在创建子类对象时,父类的无参构造函数自动被调用,这是 Java 语言的规定,无论你是否显式写出,它都会发生。
  2. 显式调用:如果你想在子类的构造函数中调用父类的有参构造函数,你必须使用 super() 关键字,并且这个调用必须是子类构造函数的第一行。
  3. super() 的位置super() 调用必须是子类构造函数中的第一条可执行语句,这意味着 this() 调用(调用本类其他构造函数)和 super() 调用不能同时存在,且 super() 必须在最前面。

详细解释与示例

隐式调用父类的无参构造函数

这是最常见的情况,当你没有使用 super() 关键字时,Java 编译器会自动在子类构造函数的第一行添加 super() 的代码。

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

示例代码:

// 父类
class Animal {
    // 父类的无参构造函数
    public Animal() {
        System.out.println("Animal 的无参构造函数被调用了");
    }
}
// 子类
class Dog extends Animal {
    // 子类的无参构造函数
    public Dog() {
        // 这里虽然没有写 super(),但编译器会自动加上 super()
        System.out.println("Dog 的无参构造函数被调用了");
    }
}
public class Main {
    public static void main(String[] args) {
        System.out.println("开始创建 Dog 对象...");
        Dog myDog = new Dog(); // 创建 Dog 对象
        System.out.println("Dog 对象创建完毕。");
    }
}

输出结果:

开始创建 Dog 对象...
Animal 的无参构造函数被调用了
Dog 的无参构造函数被调用了
Dog 对象创建完毕。

执行流程分析:

  1. new Dog() 语句执行。
  2. Java 为了确保父类的部分被正确初始化,会先去寻找并执行父类的构造函数。
  3. 因为 Dog 的构造函数中没有显式的 super(),所以默认调用 super(),即 Animal 的无参构造函数。
  4. System.out.println("Animal 的无参构造函数被调用了"); 执行。
  5. 父类 Animal 的构造执行完毕后,回到 Dog 的构造函数。
  6. System.out.println("Dog 的无参构造函数被调用了"); 执行。
  7. Dog 的构造执行完毕,对象创建成功。

显式调用父类的有参构造函数

当父类没有无参构造函数,或者你希望在子类初始化时向父类传递一些参数时,就需要显式地使用 super()

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

重要规则: super() 必须是子类构造函数的第一行。

示例代码:

// 父类
class Person {
    private String name;
    // 父类只有有参构造函数,没有无参构造函数
    public Person(String name) {
        this.name = name;
        System.out.println("Person 的有参构造函数被调用了,姓名是: " + name);
    }
}
// 子类
class Student extends Person {
    private String studentId;
    // 子类的构造函数
    public Student(String name, String studentId) {
        // 必须使用 super(name) 来调用父类的有参构造函数
        // 并且这行代码必须是第一行!
        super(name); 
        this.studentId = studentId;
        System.out.println("Student 的有参构造函数被调用了,学号是: " + studentId);
    }
}
public class Main {
    public static void main(String[] args) {
        System.out.println("开始创建 Student 对象...");
        Student myStudent = new Student("张三", "S1001");
        System.out.println("Student 对象创建完毕。");
    }
}

输出结果:

开始创建 Student 对象...
Person 的有参构造函数被调用了,姓名是: 张三
Student 的有参构造函数被调用了,学号是: S1001
Student 对象创建完毕。

执行流程分析:

  1. new Student("张三", "S1001") 执行。
  2. 进入 Student 的构造函数。
  3. 第一行代码 super("张三"); 执行,这会立即暂停 Student 的构造过程,转而去执行 Person 的构造函数 Person("张三")
  4. Person 的构造函数打印信息并执行完毕。
  5. 返回到 Student 的构造函数,接着执行 this.studentId = studentId;System.out.println(...)
  6. Student 的构造函数执行完毕,对象创建成功。

super()this() 的关系

在同一个构造函数中,super() (调用父类构造) 和 this() (调用本类其他构造函数) 不能同时使用,因为它们都必须是构造函数的第一行。

错误示例:

class Child extends Parent {
    public Child() {
        // 错误!super() 和 this() 不能同时存在
        this("some value"); 
        super("another value");
    }
    public Child(String s) {
        // ...
    }
}

正确示例:

你可以选择调用父类构造,或者调用本类其他构造(那个本类其他构造内部会再调用父类构造)。

class Parent {
    public Parent() {
        System.out.println("Parent 无参构造");
    }
    public Parent(String s) {
        System.out.println("Parent 有参构造: " + s);
    }
}
class Child extends Parent {
    // 情况一:直接调用父类构造
    public Child() {
        super(); // 可省略,效果一样
        System.out.println("Child 无参构造");
    }
    // 情况二:通过 this() 间接调用
    // this("hello") 会调用下面的 Child(String) 构造函数
    public Child() {
        this("hello"); // 调用本类的另一个构造函数
        System.out.println("Child 无参构造(通过this调用)");
    }
    public Child(String s) {
        super(s); // 在本类构造函数中,再调用父类构造
        System.out.println("Child 有参构造: " + s);
    }
}

总结与最佳实践

  1. 为什么必须调用父类构造?

    • 继承的本质:子类是父类的扩展和特化,子类对象在内存中同时包含了父类的成员变量,必须先确保父类的部分被正确初始化,然后才能初始化子类自己的部分,这保证了对象的完整性。
  2. 最佳实践

    • 为父类提供无参构造函数:这是一个非常好的编程习惯,即使父类目前没有需要初始化的成员变量,也最好提供一个显式的无参构造函数(public Parent() {}),这会让子类的编写变得非常简单和灵活,因为子类可以不写 super() 也能正确初始化。
    • 当需要时才调用有参构造:只有当你确实需要向父类传递参数来完成初始化时,才使用 super(...)
    • 永远把 super()this() 放在第一行:这是语法规则,也是为了保证初始化顺序的正确性。

通过理解这些规则和示例,你就能完全掌握 Java 中子类如何调用父类构造函数了。

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