您会看到三件事的影响:
默认超级构造函数调用,以及
与超级调用相关的实例初始值设定项,以及
重写方法如何工作
Your Sub1
构造函数是really this:
Sub1(){
super(); // <== Default super() call, inserted by the compiler
three=(int) Math.PI; // <== Instance initializers are really inserted
// into constructors by the compiler
super.printThree();
}
(令人惊讶,我知道,但这是真的。使用javap -c YourClass
看看。 :-) )
The reason看起来超类必须有机会初始化它的对象部分before子类可以初始化its物体的一部分。所以你会得到这种交织的效果。
鉴于这就是Sub1
really看起来像,让我们来看看:
JVM 创建实例并将所有实例字段设置为其默认值(所有位均关闭)。所以此时,three
字段存在,并且具有值0
.
JVM 调用Sub1
.
-
Sub1
立即致电super()
(Super1
), 哪个...
...打电话printThree
. Since printThree
被覆盖,即使对它的调用是在代码中Super1
,这是被覆盖方法(其中的一个Sub1
)被调用。这是Java 实现多态性的一部分。自从three
的实例初始化程序尚未运行,three
包含0
,这就是输出。
Super1
返回。
Back in Sub1
,实例初始化代码three
插入的编译器(实际上是重新定位的)运行并给出three
一个新的价值。
Sub1
calls printThree
. Since three
的实例初始值设定项代码现已运行,printThree
prints 3
.
关于将此实例初始值设定项代码移至构造函数中,您可能想知道:如果我有多个构造函数怎么办?代码被移入哪一个?答案是编译器重复代码到每个构造函数中。 (你可以在javap -c
,也是。)(如果您有一个非常复杂的实例初始值设定项,如果编译器有效地将其转换为方法,我不会感到惊讶,但我还没有看过。)
如果你做了一些非常顽皮的事情并在实例初始化期间调用一个方法,那就更清楚了:(实时复制 http://ideone.com/y2uGWY)
class Super
{
public static void main (String[] args) {
new Sub();
}
Super() {
System.out.println("Super constructor");
this.printThree();
}
protected void printThree() {
System.out.println("Super's printThree");
}
}
class Sub extends Super
{
int three = this.initThree();
Sub() {
this.printThree();
}
private int initThree() {
System.out.println("Sub's initThree");
return 3;
}
protected void printThree() {
System.out.println("Sub's printThree: " + this.three);
}
}
Output:
Super constructor
Sub's printThree: 0
Sub's initThree
Sub's printThree: 3
请注意“Sub's initThree”在该序列中出现的位置。