Java 构造函数 - 继承层次结构中的执行顺序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19407187/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Java Constructors - Order of execution in an inheritance hierarchy
提问by kayfun
Consider the code below
考虑下面的代码
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();
}
}
Based on my understanding of class member initialization and order of constructor execution. I expected the output to be
基于我对类成员初始化和构造函数执行顺序的理解。我希望输出是
Bread()
Cheese()
Lettuce()
Meal()
Lunch()
PortableLunch()
Sandwich()
as i believe the class members were initialized even before the main method was called. However I had the below output when i ran the program
因为我相信类成员甚至在调用 main 方法之前就已经初始化了。但是当我运行程序时我有以下输出
Meal()
Lunch()
PortableLunch()
Bread()
Cheese()
Lettuce()
Sandwich()
my confusion is whey the Meal() Lunch() and PortableLunch() run before Bread() Cheese() and Lettuce() even though their constructors where called after.
我的困惑是 Meal() Lunch() 和 PortableLunch() 在 Bread() Cheese() 和 Lettuce() 之前运行,即使它们的构造函数在后面调用。
采纳答案by Sotirios Delimanolis
These are instance fields
这些是实例字段
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce();
They only exist (execute) if an instance is created.
它们仅在创建实例时才存在(执行)。
The first thing that runs in your program is
在你的程序中运行的第一件事是
public static void main(String[] args) {
new Sandwich();
}
Super constructors are called implicitly as the first thing in each constructor, ie. before System.out.println
超级构造函数作为每个构造函数中的第一件事被隐式调用,即。前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()");}
}
After the super()
calls, instance fields are instantiated again before the constructor code.
在super()
调用之后,实例字段在构造函数代码之前再次实例化。
The order, reversed
顺序颠倒了
new Sandwich(); // prints last
// the instance fields
super(); // new PortableLunch() prints third
super(); // new Lunch() prints second
super(); // new Meal(); prints first
回答by Cruncher
private Bread b = new Bread();
private Cheese c = new Cheese();
private Lettuce l = new Lettuce();
These initializers are put into the Sandwich constructor by the compiler after the super call to it's parent class.
这些初始值设定项由编译器在对其父类进行超级调用后放入 Sandwich 构造函数中。
If these were static, then they would happen first, because static initializers happen on class load.
如果这些是静态的,那么它们将首先发生,因为静态初始值设定项在类加载时发生。
回答by Eugen Halca
even though their constructors where called after.
即使他们的构造函数在后面调用。
Not after, here how construstor method looks like to compiler :
不是之后,这里构造函数方法看起来像编译器:
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()");
}
回答by allprog
The first call in a constructor is always the super(...)
. This call is automatically inserted by the compiler if you don't write it down explicitly. No calls on the constructed object can happen before the call to super()
returned. After super()
finished, the fields are initialized in the order of appearance and then the rest of the constructor is executed.
构造函数中的第一个调用始终是super(...)
. 如果你没有明确地写下来,这个调用会被编译器自动插入。在调用super()
返回之前不能对构造的对象进行调用。完成super()
后,按出现顺序初始化字段,然后执行构造函数的其余部分。
回答by matt forsythe
I think there are two things going on here that are throwing you off. The first is that main
is a static method, where as the member variables b, c, and l are non-static instance variables. That means that they belong to the objects of the class, not the class itself. So when the class is initialized to run the main method, the contructors of Bread, Cheese, and Lettuce are not called, since no instances of Sandwich have been created.
我认为这里有两件事让你失望。第一个是main
静态方法,其中成员变量 b、c 和 l 是非静态实例变量。这意味着它们属于类的对象,而不是类本身。因此,当类被初始化为运行 main 方法时,不会调用 Bread、Cheese 和 Lettuce 的构造函数,因为没有创建 Sandwich 的实例。
Not until main actually run, and calls new Sandwich()
are any objects actually constructed. At that point, the order of operations is:
直到 main 实际运行,并且调用new Sandwich()
是实际构造的任何对象。此时,操作顺序为:
- initialize member fields of the base class(es)
- run the base class(es) constructor(s)
- initialize member fields of this class
- run the constructor of this class
- 初始化基类的成员字段
- 运行基类构造函数
- 初始化此类的成员字段
- 运行这个类的构造函数
This is done recursively, so in this case, the order would be
这是递归完成的,因此在这种情况下,顺序将是
- init fields of Meal (none)
- run constructor of Meal (prints "Meal")
- init fields of Lunch (none)
- run constructor of Lunch (prints "Lunch")
- init fields of PortableLunch (none)
- run constructor of PortableLunch (prints "PortableLunch")
- init fields of Sandwich (prints "Bread", "Cheese", and "Lettuce")
- run constructor of Sandwich (prints "Sandwich")
- Meal 的初始化字段(无)
- 运行 Meal 的构造函数(打印“Meal”)
- Lunch 的 init 字段(无)
- 运行 Lunch 的构造函数(打印“Lunch”)
- PortableLunch 的初始化字段(无)
- 运行 PortableLunch 的构造函数(打印“PortableLunch”)
- Sandwich 的 init 字段(打印“面包”、“奶酪”和“生菜”)
- 运行 Sandwich 的构造函数(打印“Sandwich”)
The purpose of this order is to ensure that the base class is fully initialized before any code in the subclass is run. This is needed because the within the constructor of the sub-class, it may call a method on the base class. Bad things would happen if that base class did not initialize its members first.
此命令的目的是确保在运行子类中的任何代码之前完全初始化基类。这是必需的,因为在子类的构造函数中,它可能会调用基类上的方法。如果该基类没有先初始化其成员,就会发生不好的事情。