对于以下代码,结果为
我在B中,值为0
我在B,值是44
22
public class Test {
public static void main(String[] args) {
P b = new B();
System.out.println(b.a);
}
static class P {
public int a = 11;
public P() {
a = 22;
diplay();
}
public void diplay() {
System.out.println("I am in P, value is " + a);
}
}
static class B extends P {
int a = 33;
public B() {
a = 44;
diplay();
}
public void diplay() {
System.out.println("I am in B, value is " + a);
}
}
}
首先,为什么构造函数被调用两次?
为什么b.a
的值为22
?
最后,为什么第一个a
是值0
?
答案 0 :(得分:7)
无论何时您不提供对超类构造函数的显式调用,Java编译器都会为您插入对默认超类构造函数的隐式调用(不带参数)。就像您的B
构造函数确实是这样:
public B() {
super();
a = 44;
diplay();
}
对超类构造函数的调用将调用P
构造函数,该构造函数将调用diplay
。该对象实际上是B
,因此,通过多态,将调用B
的{{1}}方法。
这时,您已经泄漏您的子类实例,因为它尚未完全构建。因此,diplay
的变量{{1 }}隐藏B
的变量a
,但尚未初始化,因此其默认值仍为P
。
然后,超类a
构造函数完成,其余0
构造函数运行,该构造函数也调用P
。此调用将看到已初始化的B
值。
构造函数不会被调用两次;子类构造函数diplay
隐式调用超类构造函数44
,两个构造函数都调用B
。
返回P
,您引用字段diplay
,但是引用位于类型main
的变量上。没有字段多态性,因此即使对象在运行时是a
,也将检索P
的{{1}}的值,并将其初始化为B
。 / p>
这段代码说明了为什么通常不是一个好主意
P
对象实例,并且答案 1 :(得分:1)
首先,调用父类class P
的构造函数。然后,它调用diplay()
。因为您正在创建class B
的实例,所以B::diplay()
称为打印I am in B, value is 0
,因为a
中定义的变量B
仅用默认值0和{{ 1}}尚未执行。在执行a = 33
之后,将执行a = 33
的构造函数并打印B
。
这是所有这些调用的顺序:
呼叫顺序如下:
1。超类的静态块*
2。类的静态块*
3。超类的非静态块*
4。超类的构造函数
5。该类的非静态块*
6。类的构造函数
https://javacertificationroadmap.com/class-initialization-and-inheritance/