当我调用printThread时,为什么要打印0和3?
class Super {
Super() {
three();
}
void three() {
System.out.println("three");
}
}
class Child extends Super {
int three = (int) Math.PI; // That is, 3
void three() {
System.out.println(three);
}
public static void main(String[] args) {
Child t = new Child();
t.three();
}
}
输出为0和3 但它应该是3和3
答案 0 :(得分:1)
当您致电new SomeType(..)
时,首先new
会创建SomeType
类的实例,其字段设置为default values (适用于{{ 1}}对于int
0
,对象引用boolean
)为false
。
稍后通过 构造函数 代码完成对象的正确初始化。这就是为什么负责初始化字段的代码在每个构造函数的开头移动(在null
调用之后,因为子类中的代码通常依赖于超类设置)。所以
super()
编译为
class Child extends Super {
int three = (int) Math.PI; // That is, 3
由于在class Child extends Super {
int three = 0;//initialization to proper value is moved to constructors
// |
Child(){ // |
super(); // |
three = (int) Math.PI; //<--------------------+
}
...
}
字段正确初始化之前调用了super()
,因此其值仍设置为three
。
超类的构造函数调用0
方法但由于它在Child类中被重写,因为polymorphism,调用了three();
的代码。由于该代码的打印值为Child#three
,并且尚未进行正确的初始化,因此您首先看到其默认值three
(由0
运算符设置)。
构造函数完成作业后,您通过new
第二次调用three();
。此时t.three();
已正确初始化为three
(3
的结果),因此您看到了(int) Math.PI;
。
为避免此类问题,请不要在构造函数中使用可在子类中重写的方法。直接使用字段,或使用私有,最终或静态方法。
答案 1 :(得分:0)
如果您单步执行此操作,three()
将永远是子类的方法(您永远不会调用Super
中的实现)。
此外,three()
中对Super()
的第一次调用实际上将在Child
的其余构造函数之前运行,因此第一次调用为0(子对象不是完全初始化了。)