【问题标题】:Java Constructors - Order of execution in an inheritance hierarchyJava 构造函数 - 继承层次结构中的执行顺序
【发布时间】:2013-10-24 18:57:42
【问题描述】:

考虑下面的代码

  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()

因为我相信类成员甚至在调用 main 方法之前就已初始化。但是,当我运行程序时,我得到了以下输出

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

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

【问题讨论】:

标签: java


【解决方案1】:

这些是实例字段

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

【讨论】:

【解决方案2】:

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

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

  1. 初始化基类的成员字段
  2. 运行基类构造函数
  3. 初始化该类的成员字段
  4. 运行这个类的构造函数

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

  1. Meal 的初始化字段(无)
  2. 运行 Meal 的构造函数(打印“Meal”)
  3. Lunch 的初始化字段(无)
  4. 运行Lunch 的构造函数(打印“Lunch”)
  5. PortableLunch 的初始化字段(无)
  6. 运行 PortableLunch 的构造函数(打印“PortableLunch”)
  7. Sandwich 的初始化字段(打印“Bread”、“Cheese”和“Lettuce”)
  8. 运行 Sandwich 的构造函数(打印“Sandwich”)

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

【讨论】:

    【解决方案3】:

    即使它们的构造函数是在之后调用的。

    不是之后,这里是 contrustor 方法在编译器中的样子:

    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()");
    }
    

    【讨论】:

      【解决方案4】:

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

      【讨论】:

        【解决方案5】:
        private Bread b = new Bread();
        private Cheese c = new Cheese();
        private Lettuce l = new Lettuce();
        

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

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

        【讨论】:

          猜你喜欢
          • 2011-10-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-11-24
          • 2013-01-29
          相关资源
          最近更新 更多