Java构造函数 – inheritance层次结构中的执行顺序

考虑下面的代码

class Meal { Meal() { System.out.println("Meal()"); } } class Bread { Bread() { System.out.println("Bread()"); } } class Cheese { Cheese() { System.out.println("Cheese()"); } } class Lettuce { Lettuce() { System.out.println("Lettuce()"); } } class Lunch extends Meal { Lunch() { System.out.println("Lunch()"); } } class PortableLunch extends Lunch { PortableLunch() { System.out.println("PortableLunch()");} } class Sandwich extends PortableLunch { private Bread b = new Bread(); private Cheese c = new Cheese(); private Lettuce l = new Lettuce(); public Sandwich() { System.out.println("Sandwich()"); } public static void main(String[] args) { new Sandwich(); } } 

基于我对类成员初始化和构造函数执行顺序的理解。 我期待输出

 Bread() Cheese() Lettuce() Meal() Lunch() PortableLunch() Sandwich() 

因为我相信甚至在主要方法被调用之前就已经初始化了class级成员。 但是当我运行程序时,我有以下输出

 Meal() Lunch() PortableLunch() Bread() Cheese() Lettuce() Sandwich() 

我的困惑是乳清Meal()Lunch()和PortableLunch()在Bread()Cheese()和Lettuce()之前运行,即使它们的构造函数在之后调用。

这些是实例字段

 private Bread b = new Bread(); private Cheese c = new Cheese(); private Lettuce l = new Lettuce(); 

它们仅在创建实例时存在(执行)。

在你的程序中运行的第一件事是

 public static void main(String[] args) { new Sandwich(); } 

超级构造函数被隐式调用为每个构造函数中的第一个东西,即。 在System.out.println之前

 class Meal { Meal() { System.out.println("Meal()"); } } class Lunch extends Meal { Lunch() { System.out.println("Lunch()"); } } class PortableLunch extends Lunch { PortableLunch() { System.out.println("PortableLunch()");} } 

super()调用之后,实例字段在构造函数代码之前再次实例化。

顺序,逆转

 new Sandwich(); // prints last // the instance fields super(); // new PortableLunch() prints third super(); // new Lunch() prints second super(); // new Meal(); prints first 

即使他们的构造者在那里被召唤。

不是之后,这里的construstor方法如何看起来像编译器:

 public Sandwich(){ super();// note this calls super constructor, which will call it's super and so on till Object's constructor //initiate member variables System.out.println("Sandwich()"); } 

我认为这里有两件事让你失望。 第一个是main是静态方法,其中成员变量b,c和l是非静态实例变量。 这意味着它们属于类的对象,而不是类本身。 因此,当初始化类以运行main方法时,不会调用Bread,Cheese和Lettuce的构造函数,因为没有创建三明治实例。

直到main实际运行,并且调用new Sandwich()是实际构造的任何对象。 那时,操作的顺序是:

  1. 初始化基类的成员字段
  2. 运行基类(s)构造函数
  3. 初始化此类的成员字段
  4. 运行此类的构造函数

这是递归完成的,所以在这种情况下,顺序就是

  1. 膳食的初始字段(无)
  2. 运行Meal的构造函数(打印“Meal”)
  3. 初始午餐领域(无)
  4. 运行午餐的构造函数(打印“午餐”)
  5. PortableLunch的init字段(无)
  6. 运行PortableLunch的构造函数(打印“PortableLunch”)
  7. 三明治的初始字段(打印“面包”,“奶酪”和“生菜”)
  8. 运行三明治的构造函数(打印“三明治”)

此顺序的目的是确保在运行子类中的任何代码之前完全初始化基类。 这是必需的,因为在子类的构造函数内,它可以调用基类的方法。 如果该基类没有首先初始化其成员,那么会发生不好的事情。

构造函数中的第一个调用始终是super(...) 。 如果您没有明确地将其写下,则编译器会自动插入此调用。 在调用super()返回之前,不会对构造对象进行调用。 在super()完成之后,按照出现的顺序初始化字段,然后执行构造函数的其余部分。

 private Bread b = new Bread(); private Cheese c = new Cheese(); private Lettuce l = new Lettuce(); 

在超级调用它的父类之后,编译器将这些初始化程序放入Sandwich构造函数中。

如果这些是静态的,那么它们将首先发生,因为静态初始化器发生在类加载上。