我在Java测试中遇到一个问题,但我错了。请向我解释原因。
public class A {
protected String str = "A";
public static int i = 1;
public A() {
foo();
}
public void foo() {
// TODO Auto-generated method stub
System.out.println(str + " " + i);
}
}
public class B extends A {
protected String str = "B";
public static int i = 2;
public B() {
foo();
}
public void foo() {
// TODO Auto-generated method stub
System.out.println(str + " " + i);
}
}
public class Program {
public static void main(String args[]){
A a = new B();
B b = new B();
}
}
在测试中,他们问我输出是什么? 我回答:“ A 1 B 2 A 1 B 2” 但是正确的答案是:“ null 2 B 2 null 2 B 2” 你能解释为什么吗?
答案 0 :(得分:0)
对于第A a = new B();
行,输出为null 2 B 2
,因为在隐式super()
完成运行之前,类中的实例字段尚未初始化。
在这种情况下,由于class B
正在扩展class A
,因此B()
构造函数将通过A()
隐式调用超类无参数构造函数super()
,其中foo()
方法被调用。
public A() { <---
foo(); //overriden version in class B is called |
} |
|
public B() { |
// Static fields are already initialized |
// Instance fields are not yet initialized |
// Here super() is called implicitly which calls A() ---
// Instance fields are now initialized to respective values
foo();
}
调用foo()
中被覆盖的class B
,由于foo()
中class B
被覆盖,因为super()
尚未完成,实例字段class B
中的尚未初始化,而是null
。因此,您将看到第一个null 2
。
然后,当控件从super()
中出来时,实例初始化程序将为B
运行,然后调用foo()
的{{1}},此时{{ 1}}初始化为值class B
,因此您看到class B
已打印。
您看不到静态字段的任何问题,因为它们不依赖于完成B
的调用,它们在加载类时已初始化,因此您看到了{{1 }}始终进行初始化和打印,而不是B 2
。
super()
行也是如此。
要注意的重要一点是,您已经在类i
中重新定义了null
实例字段,因此,当调用覆盖的B b = new B();
时,它实际上将引用该实例字段的实例字段。类str
而不是类B
的字段。
考虑以下代码,我从类foo()
中删除了B
字段:
A
这将在您重新运行程序时在下面显示输出:
str
之所以发生这种情况,是因为B
的{{1}}调用了构造函数class A {
protected String str = "A";
public static int i = 1;
public A() {
foo();
}
public void foo() {
// TODO Auto-generated method stub
System.out.println(str + " " + i);
}
}
class B extends A {
public static int i = 2;
public B() {
foo();
}
public void foo() {
// TODO Auto-generated method stub
System.out.println(str + " " + i);
}
}
,而后者又通过自己的A 2
A 2
A 2
A 2
调用了super()
构造函数。在B
中完成A()
之后,将从Object()
继承的super()
的实例成员全部初始化。现在,覆盖的super()
将打印出从A()
继承的B
的初始化值。
答案 1 :(得分:0)
new B()
将尝试使用B的构造函数创建B的新实例。所有构造函数始终以super()
调用开头,即使该调用不存在。除此之外,还有“实例初始化程序”;这些是分配给str
变量的表达式。它们不是字符串常量,因为它需要一个静态的最终变量,而str
不是。
方法重载,但字段不重载:每个类都有自己的名为str
的字段,与另一个str
的字段无关。
对象创建的执行顺序如下:
所以,按顺序:
A.str
,B.str
都设置为null。A.str = "A"
被评估。foo()
。 foo是一种方法,因此使用动态分配。 B覆盖它,它是B的一个实例,因此,运行B的foo()而不是A的foo()。str
,它仍然为空。null 2
B.str = "B"
。B.str
已初始化,因此它打印B 2
。null 2 B 2
将再次打印。