使用inheritance和重写方法在Java中复杂输出

我偶然发现了这段代码。
我试着猜测在实际操作之前运行它的结果是什么。 当我看到它们并需要一些解释时,我真的很困惑。
这是代码:

public class A { String bar = "A.bar"; A() { foo(); } public void foo() { System.out.println("A.foo(): bar = " + bar); } } public class B extends A { String bar = "B.bar"; B() { foo(); } public void foo() { System.out.println("B.foo(): bar = " + bar); } } public class C { public static void main(String[] args) { A a = new B(); System.out.println("a.bar = " + a.bar); a.foo(); } } 

输出是:

 B.foo(): bar = null B.foo(): bar = B.bar a.bar = A.bar B.foo(): bar = B.bar 

这是为什么?

  • bar = null怎么样?
  • 为什么a.bar = A.bar甚至出现? 我根本没有实例化A
  • 如果A出现,为什么它会 B 之后呢?

在我开始解释代码执行过程中的每一步之前,您应该了解一些事实:

  • 基于引用类型解析字段引用,并且基于对象类型在运行时(以动态方式)解析方法调用。
  • 即使你没有把它放在那里, super()也会隐式放在每个构造函数中(例如,如果你调用super(int x, int y)则不调用它)。
  • 从构造函数中调用“覆盖能力”方法被认为是非常糟糕的做法 – 当我们执行时,您将看到原因。

现在让我们一步一步地分解您的代码:

  • 您通过调用其默认构造函数B()实例化B
  • 正如我之前所说的,对super()的调用被隐式添加到任何构造函数中,因此立即调用A()
  • A()你调用foo() ,它在B类中被重写,这就是从B调用foo()原因。
  • Bfoo()你得到输出B.foo(): bar = null因为Java还没有初始化B的字段(它的构造函数还没有被执行!)和对象类型的字段默认情况下初始化为null
  • 现在我们已经完成了A()的构造函数,我们回到B()的构造函数。
  • 在所述构造函数中,我们再次调用foo() ,这又是Bfoo() 。 但是与上次不同, B的字段已经初始化(在调用super() ),因此你得到了预期的B.foo(): bar = B.bar
  • 现在我们又回到了main的温暖怀抱。
  • 你访问a.bar ,因为我说的字段引用是根据引用类型解析的,你得到A的字段bar
  • 最后,出于同样的原因,你调用a.foo()再次触发Bfoo()再次打印b.bar

我们完成了! 🙂

进一步参考和有价值的阅读材料:
静态和动态绑定解释
构造函数调用的顺序