核心要点
- 隐式调用:在创建子类对象时,父类的无参构造函数会自动被调用,这是 Java 语言的规定,无论你是否显式写出,它都会发生。
- 显式调用:如果你想在子类的构造函数中调用父类的有参构造函数,你必须使用
super()关键字,并且这个调用必须是子类构造函数的第一行。 super()的位置:super()调用必须是子类构造函数中的第一条可执行语句,这意味着this()调用(调用本类其他构造函数)和super()调用不能同时存在,且super()必须在最前面。
详细解释与示例
隐式调用父类的无参构造函数
这是最常见的情况,当你没有使用 super() 关键字时,Java 编译器会自动在子类构造函数的第一行添加 super() 的代码。

示例代码:
// 父类
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 对象创建完毕。
执行流程分析:
new Dog()语句执行。- Java 为了确保父类的部分被正确初始化,会先去寻找并执行父类的构造函数。
- 因为
Dog的构造函数中没有显式的super(),所以默认调用super(),即Animal的无参构造函数。 System.out.println("Animal 的无参构造函数被调用了");执行。- 父类
Animal的构造执行完毕后,回到Dog的构造函数。 System.out.println("Dog 的无参构造函数被调用了");执行。Dog的构造执行完毕,对象创建成功。
显式调用父类的有参构造函数
当父类没有无参构造函数,或者你希望在子类初始化时向父类传递一些参数时,就需要显式地使用 super()。

重要规则: 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 对象创建完毕。
执行流程分析:
new Student("张三", "S1001")执行。- 进入
Student的构造函数。 - 第一行代码
super("张三");执行,这会立即暂停Student的构造过程,转而去执行Person的构造函数Person("张三")。 Person的构造函数打印信息并执行完毕。- 返回到
Student的构造函数,接着执行this.studentId = studentId;和System.out.println(...)。 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);
}
}
总结与最佳实践
-
为什么必须调用父类构造?
- 继承的本质:子类是父类的扩展和特化,子类对象在内存中同时包含了父类的成员变量,必须先确保父类的部分被正确初始化,然后才能初始化子类自己的部分,这保证了对象的完整性。
-
最佳实践:
- 为父类提供无参构造函数:这是一个非常好的编程习惯,即使父类目前没有需要初始化的成员变量,也最好提供一个显式的无参构造函数(
public Parent() {}),这会让子类的编写变得非常简单和灵活,因为子类可以不写super()也能正确初始化。 - 当需要时才调用有参构造:只有当你确实需要向父类传递参数来完成初始化时,才使用
super(...)。 - 永远把
super()或this()放在第一行:这是语法规则,也是为了保证初始化顺序的正确性。
- 为父类提供无参构造函数:这是一个非常好的编程习惯,即使父类目前没有需要初始化的成员变量,也最好提供一个显式的无参构造函数(
通过理解这些规则和示例,你就能完全掌握 Java 中子类如何调用父类构造函数了。
