扩展类的构造函数被调用两次

时间:2018-06-28 01:15:23

标签: java constructor

对于以下代码,结果为

  

我在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

2 个答案:

答案 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>

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

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

答案 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/