public class Base {
public Base() {
x = 0;
bar();
}
public Base(int x) {
this.x = x;
foo();
}
public void foo() {
System.out.println("Base.foo : " + x);
}
private void bar() {
System.out.println("Base.bar:" + x.toString());
}
protected Integer x;
}
public class Derived extends Base {
public Derived() {
bar();
}
public Derived(int x, int y) {
super(x);
this.y = y;
}
public void foo() {
System.out.println("Derived.foo : " + x + ", " + y);
}
public void bar() {
System.out.println("Derived.bar:" + x.toString() + ", " + y.toString());
}
private Integer y;
public static void main(String[] args) {
Base b = new Derived(10, 20);
}
}
为什么打印"Derived.foo:" 10, nulll
而不是20而不是null?
y是Derived的私有变量,它用20初始化。它在它的范围内..那么为什么它为null?
答案 0 :(得分:7)
因为首先调用超级构造函数(super(x)
)。这个超级构造函数调用方法foo。然后Derived构造函数将y初始化为20(this.y = y
)。因此,当调用foo时,y
尚未初始化。
在构造函数中调用可覆盖的方法是一种不好的做法,因为正如您刚才注意到的,它可以在部分构造的对象上调用重写方法。
答案 1 :(得分:2)
println
来自方法foo
,它是从Base
的构造函数调用的,它是从Derived
的构造函数调用的(通过super
) 之前初始化y
。所以期望空值。
答案 2 :(得分:2)
这样做是因为超类(Base
)构造函数在设置Derived.foo()
成员变量之前调用y
。
所以这是基本的操作顺序:
main(...)
Derived(10,20) (start constructor)
Base(10) (start constructor)
this.x = x
foo() (calls Derived.foo() which prints the message you see)
之后
this.y = y
答案 3 :(得分:2)
如果您按照构造函数的调用,它会更清楚:
Derived(int x, int y)
构造函数Base(int x)
x
变量foo()
方法println()
y
变量如您所见,在您尝试打印出值后,y变量已设置。这就是为什么你看到null
的未初始化值。
从构造函数中调用可覆盖的方法是一个常见的错误,因为它可以在完全构造对象之前允许未初始化的变量转义。
答案 4 :(得分:1)
当你进入超级类y
的构造函数时,尚未实例化。因此,对foo()
的调用将包含null
y
。
答案 5 :(得分:1)
由Super-Constructor调用触发的Derived.foo()
打印,之后用20初始化。所以在打印时它仍然是空的。
答案 6 :(得分:1)
所有的答案确实都是正确的,但下次你可以在打印东西的函数中设置一个断点。 然后,您可以通过读取正在调用的部分来检查调用堆栈并查看代码。
这样你就可以追踪y不会被初始化。
祝你好运! 罗埃尔答案 7 :(得分:1)
因为在Base类构造函数中调用foo()时y尚未初始化。
链条走了 主要 - >派生(x,y) - >基数(x) - >初始化x,foo()。但是,因为你在Derived中启动调用,它会覆盖foo(),派生的foo()实际上是由解释器执行的。