扩展类的构造器被调用两次



对于以下代码,结果为

我在 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.a22
最后,为什么第一个a0

只要您不提供对超类构造函数的显式调用,Java 编译器就会为您插入对默认超类构造函数的隐式调用(无参数(。 就好像你的B构造函数真的是:

public B() {
super();
a = 44;
diplay();
}

对超类构造函数的调用调用P构造函数,后者调用diplay。 对象实际上是一个B,所以多态性,调用Bdiplay方法。

此时,您已经泄漏了子类实例,因为它尚未完全构造。正因为如此,隐藏P变量aB的变量a还没有初始化,所以它仍然有它的默认值0

然后超类P构造函数完成,其余的B构造函数运行,这也调用diplay。 此调用将看到初始化的44值。

构造函数不被调用两次;子类构造函数B隐式调用超类构造函数P,并且两个构造函数都调用diplay

回到main,你引用字段a,但引用是在类型为P的变量上。 没有场多态性,所以即使对象在运行时是B,也会检索Pa的值,该值被初始化为22

这段代码显示了为什么通常不是一个好主意

  1. 在构造函数完成之前泄漏this对象实例,以及
  2. 有意在子类中声明与超类中同名的变量。

首先,调用父类class P的构造函数。然后它调用diplay()。因为您正在创建class BB::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/

最新更新