Java继承中,构造器和变量初始化的执行顺序究竟是怎样的?

深入探讨Java继承中的构造器与变量初始化顺序

Java继承机制中,构造器和变量的初始化顺序并非总是直观易懂。虽然《On Java》提到变量会在任何方法(包括构造器)调用前初始化,但JVM的实际执行顺序可能出乎意料。

让我们通过一个简化示例来分析:

class Shape {
    Shape() {
        System.out.println("Shape");
    }
}

class Line extends Shape {
    Line() {
        super();
        System.out.println("Line");
    }
}

public class CADSystem extends Shape {
    private Line li = new Line(); // 关键点:成员变量初始化
    public CADSystem() {
        super();
        System.out.println("CADSystem");
    }
}

运行结果:

Shape
Shape
Line
CADSystem

很多人会误以为执行顺序是:

  1. 创建Line对象,调用Line构造器,输出"Shape"、"Line"。
  2. 调用CADSystem构造器,在li初始化前输出"Shape"。
  3. 输出"CADSystem"。

然而,JVM的实际执行顺序遵循以下规则:

  1. 父类初始化优先: 首先,CADSystem继承自Shape,因此JVM会先初始化Shape类,输出"Shape"。
  2. 成员变量初始化: 接下来,JVM初始化CADSystem的成员变量li。这会调用Line的构造器。
  3. super()调用: Line构造器中包含super(),这会在Line构造器体执行前调用父类Shape的构造器,再次输出"Shape"。
  4. Line构造器体执行: 然后,Line构造器体执行,输出"Line"。
  5. CADSystem构造器执行:

    最后,CADSystem构造器执行,输出"CADSystem"。

因此,理解的关键在于:父类构造器的调用发生在子类成员变量初始化之前,而super()的调用发生在子类构造器体之前。 这决定了最终的执行顺序。 成员变量的初始化并非简单的在构造器之前,而是与父类构造器的调用顺序紧密相连。