
本文深入解析java中类构造器的调用机制与执行顺序。通过this()和super()关键字,构造器在继承体系中形成调用链。每个构造器都必须显式或隐式地调用父类构造器或同类其他构造器。文章将详细阐述这一过程,并结合示例代码分析构造器中语句的实际执行顺序,揭示为何特定输出模式会产生。
理解Java构造器与继承
在Java中,构造器是用于创建和初始化对象的特殊方法。当一个类继承自另一个类时,子类的构造器在执行其自身逻辑之前,必须先确保其父类部分被正确初始化。这一机制通过构造器链(Constructor Chaining)来实现,它规定了构造器在类继承层次结构中的调用顺序。
构造器链的核心规则
Java对构造器的调用有着严格的规定,以确保对象在继承体系中的完整性。以下是核心规则:
- 强制调用 this() 或 super(): 每个构造器的第一条语句,如果不是显式地调用 this()(调用本类的其他构造器)或 super()(调用父类的构造器),Java编译器会自动在第一行插入一个 super(); 调用。
- super(); 的默认行为: 编译器自动插入的 super(); 会尝试调用父类的无参数构造器。
- 父类无参构造器的要求: 如果父类没有可访问的无参数构造器(例如,只定义了带参数的构造器而没有定义无参构造器),且子类构造器没有显式调用 this() 或 super(),则编译会失败。
- this() 和 super() 的互斥性与位置: 一个构造器内部,this() 和 super() 只能出现一个,且必须是该构造器的第一条语句。它们不能同时出现,也不能出现在第一条语句之后。
- java.lang.Object 的特殊性: java.lang.Object 类是所有Java类的最终父类。它的构造器是唯一一个不需要调用 super() 的构造器,因为它没有父类。
这些规则共同确保了从继承链顶端(Object)到底端(当前子类)的所有父类部分都能被正确地初始化。
示例代码分析
为了更深入地理解构造器的调用顺序,我们来看一个具体的代码示例:
立即学习“Java免费学习笔记(深入)”;
public class Test {
public static void main(String[] args) {
new Circle9();
}
}
class GeometricObject {
GeometricObject() {
System.out.print("A");
}
public GeometricObject(String color, boolean filled) {
System.out.print("B");
}
}
class Circle9 extends GeometricObject {
public Circle9() {
this(1.0);
System.out.print("C");
}
public Circle9(double radius) {
this(radius, "white", false);
System.out.print("D");
}
public Circle9(double radius, String color, boolean filled) {
super(color, filled);
System.out.print("E");
}
}现在,我们来一步步追踪 new Circle9(); 的执行流程:
-
new Circle9();
- 这个语句触发了 Circle9 类的无参数构造器:public Circle9()。
-
执行 public Circle9() 构造器
- 该构造器的第一条语句是 this(1.0);。这意味着它会立即调用 Circle9 类的单参数构造器 public Circle9(double radius)。
- 重要提示: System.out.print("C"); 语句不会立即执行,它会等待 this(1.0); 所调用的构造器及其完整的构造器链执行完毕并返回后,才会被执行。
-
执行 public Circle9(double radius) 构造器
- 该构造器的第一条语句是 this(radius, "white", false);。这意味着它会立即调用 Circle9 类的三参数构造器 public Circle9(double radius, String color, boolean filled)。
- 重要提示: System.out.print("D"); 语句会等待 this(radius, "white", false); 所调用的构造器及其完整的构造器链执行完毕并返回后,才会被执行。
-
执行 public Circle9(double radius, String color, boolean filled) 构造器
- 该构造器的第一条语句是 super(color, filled);。这意味着它会立即调用父类 GeometricObject 的双参数构造器 public GeometricObject(String color, boolean filled)。
- 重要提示: System.out.print("E"); 语句会等待 super(color, filled); 所调用的构造器及其完整的构造器链执行完毕并返回后,才会被执行。
-
执行 public GeometricObject(String color, boolean filled) 构造器
- 该构造器的第一条语句没有显式调用 this() 或 super()。根据规则,Java编译器会在这里隐式插入 super();。这个隐式的 super(); 会调用 java.lang.Object 类的无参数构造器。
- java.lang.Object 的构造器执行完毕并返回。
- 现在,public GeometricObject(String color, boolean filled) 构造器继续执行其自身的代码,打印 "B"。
- 该构造器执行完毕并返回。
至此,最深层的构造器调用链已经完成,程序开始回溯,并执行之前被暂停的打印语句:
- 从 public GeometricObject(String color, boolean filled) 返回后,public Circle9(double radius, String color, boolean filled) 构造器继续执行,打印 "E"。
- 从 public Circle9(double radius, String color, boolean filled) 返回后,public Circle9(double radius) 构造器继续执行,打印 "D"。
- 从 public Circle9(double radius) 返回后,public Circle9() 构造器继续执行,打印 "C"。
最终,程序的输出结果是:BEDC。
为什么 "A" 没有被打印?
从上述详细的执行流程可以看出,GeometricObject 类中定义了一个无参数构造器 GeometricObject(),它负责打印字符 "A"。然而,在整个 Circle9 对象的创建过程中,Circle9 类的构造器最终通过 super(color, filled); 调用的是 GeometricObject 类的双参数构造器 public GeometricObject(String color, boolean filled)。由于构造器链中,一个子类构造器只能调用一个直接父类构造器(通过 super() 或隐式的 super()),因此 GeometricObject() (打印 "A" 的那个) 并没有被任何 this() 或 super() 调用到。它的代码逻辑从未被执行,所以字符 "A" 也永远不会被打印。
总结与注意事项
- 构造器链的强制性: Java通过强制的构造器链机制,确保了在对象实例化时,所有父类的部分都能按照自顶向下的顺序得到初始化。
- this() 和 super() 的作用: 它们是控制构造器链方向的关键。this() 用于在同类中重定向到其他构造器,实现构造器重载的复用;super() 用于向上调用父类构造器,完成父类部分的初始化。
- 执行顺序: 构造器中 this() 或 super() 调用会立即发生,并等待被调用的构造器及其完整的构造器链执行完毕后,才会返回并执行当前构造器中 this()/super() 之后的语句。这意味着,父类构造器的代码逻辑总是在子类构造器的代码逻辑之前执行(不考虑 this() 内部跳转)。
- 隐式 super(); 的影响: 当构造器中没有显式调用 this() 或 super() 时,编译器会自动插入 super();。如果父类没有可访问的无参构造器,这会导致编译错误。因此,在定义带参数的构造器时,如果需要无参构造器,应显式提供。
- 设计考量: 在设计类和继承关系时,应仔细考虑构造器的参数和调用逻辑,确保对象能被正确且一致地初始化。明确哪些父类构造器会被调用,以及它们何时执行,对于避免初始化问题至关重要。
通过深入理解Java构造器链的机制,开发者可以更好地控制对象的创建过程,避免因构造器调用顺序不当而导致的初始化问题,并编写出更健壮、可维护的代码。










