使用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()
原因。 - 在
B
的foo()
你得到输出B.foo(): bar = null
因为Java还没有初始化B
的字段(它的构造函数还没有被执行!)和对象类型的字段默认情况下初始化为null
。 - 现在我们已经完成了
A()
的构造函数,我们回到B()
的构造函数。 - 在所述构造函数中,我们再次调用
foo()
,这又是B
的foo()
。 但是与上次不同,B
的字段已经初始化(在调用super()
),因此你得到了预期的B.foo(): bar = B.bar
。 - 现在我们又回到了
main
的温暖怀抱。 - 你访问
a.bar
,因为我说的字段引用是根据引用类型解析的,你得到A
的字段bar
。 - 最后,出于同样的原因,你调用
a.foo()
再次触发B
的foo()
再次打印b.bar
。
我们完成了! 🙂
进一步参考和有价值的阅读材料:
静态和动态绑定解释
构造函数调用的顺序