对于以下代码,结果为
我在 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
?
只要您不提供对超类构造函数的显式调用,Java 编译器就会为您插入对默认超类构造函数的隐式调用(无参数(。 就好像你的B
构造函数真的是:
public B() {
super();
a = 44;
diplay();
}
对超类构造函数的调用调用P
构造函数,后者调用diplay
。 对象实际上是一个B
,所以多态性,调用B
的diplay
方法。
此时,您已经泄漏了子类实例,因为它尚未完全构造。正因为如此,隐藏P
变量a
的B
的变量a
还没有初始化,所以它仍然有它的默认值0
。
然后超类P
构造函数完成,其余的B
构造函数运行,这也调用diplay
。 此调用将看到初始化的44
值。
构造函数不被调用两次;子类构造函数B
隐式调用超类构造函数P
,并且两个构造函数都调用diplay
。
回到main
,你引用字段a
,但引用是在类型为P
的变量上。 没有场多态性,所以即使对象在运行时是B
,也会检索P
a
的值,该值被初始化为22
。
这段代码显示了为什么通常不是一个好主意
- 在构造函数完成之前泄漏
this
对象实例,以及 - 有意在子类中声明与超类中同名的变量。
首先,调用父类class P
的构造函数。然后它调用diplay()
。因为您正在创建class B
B::diplay()
实例,所以称为打印I am in B, value is 0
因为B
中定义的变量a
仅使用默认值 0 初始化,并且尚未执行a = 33
。执行该a = 33
后,执行B
的构造函数并打印I am B, value 44
。 以下是所有这些调用的顺序:
调用顺序如下:
1.超类的静态块*
2.类的静态块*
3.超类的非静态块*
4.超类的构造函数
5.类的非静态块*
6.类的构造函数
https://javacertificationroadmap.com/class-initialization-and-inheritance/