使用继承和覆盖方法在Java中复杂输出 [英] Perplexing output in Java with inheritance and overriding methods
本文介绍了使用继承和覆盖方法在Java中复杂输出的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我偶然发现了这段代码。
我试图猜测在运行它之前会有什么结果。
我真的很困惑,当我看到他们&需要一些解释。
这是代码:
I stumbled upon this piece of code.
I tried to guess what will be the result of running it before actually doing so.
I was really confused when I saw them & in need of some explanations.
This is the code:
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
之后
- How is
bar = null
? - Why is
a.bar = A.bar
even appear? I haven't instantiatedA
at all. - And if
A
appears, why is it afterB
?
推荐答案
在我开始解释代码执行的每一步之前,你应该知道几个事实:
There are a few facts you ought to know before I start explaining every single step in your code's execution:
- 字段引用是基于引用类型解析的,并且基于对象类型在运行时(以动态方式)解析方法调用。
-
即使你没有自己把它放在每个构造函数中,也会将 隐藏 c $ c> super(int x,int y)
例如)。
- Field references are resolved based on reference type and method calls are resolved during run-time (in a dynamic-fashion) based on the object type.
super()
is placed implicitly in every constructor even if you don't put it there yourself (it is not called if you callsuper(int x, int y)
for instance).- It is considered very bad practice to call "override-able" methods from a constructor - you will see why when we go through the execution.
现在让我们一步步分解你的代码:
Now let's break down your code step-by-step:
- 通过调用其默认构造函数
B()来实例化
B
- 如前所述,调用
super()
/ em>到任何构造函数,所以A()
被立即调用 - / code>你调用
foo()
,它在类B
中被覆盖,这就是为什么<$从B
。 -
调用
foo()的 foo()
你得到输出B.foo():bar = null $ c $因为Java还没有初始化
B
的字段(它的构造函数还没有被执行!),对象类型的字段被初始化为<$现在我们已经完成了A()的构造函数
>我们回到B()
的构造函数。 - 在构造函数中,我们调用
foo()
,再次B
'但与上一次不同,
B
的字段已初始化(在调用super()
后)预期B.foo():bar = B.bar
。 - 现在我们回到了
main
。 - 您访问
a.bar
基于引用类型解析,您得到A
的字段bar
。 - 最后,出于同样的原因,你调用
a.foo()
,它再次触发B
'foo()
,再次打印b.bar
。
- You instantiate
B
by calling its default constructorB()
. - As I said before, the call to
super()
is added implicitly to any constructor, soA()
is immediately called. - Inside
A()
you callfoo()
, which is overridden in classB
and that is whyfoo()
is called fromB
. - Inside
B
'sfoo()
you get the outputB.foo(): bar = null
since Java didn't get to initializeB
's fields yet (its constructor hasn't been executed yet!) and the fields of object type are initialized tonull
by default. - Now that we are done with the constructor of
A()
we go back to the constructor ofB()
. - Inside said constructor, we have the call to
foo()
again, which is againB
'sfoo()
. But different from last time,B
have its fields initialized (after the call tosuper()
) properly so you get the expectedB.foo(): bar = B.bar
. - Now we are back to the warm embrace of
main
. - You access
a.bar
, and since as I said field references are resolved based on reference type, you get the fieldbar
ofA
. - Lastly, for the same reason, you call
a.foo()
which again triggersB
'sfoo()
which printsb.bar
once again.
我们完成了! :)
更多参考资料和值得阅读的资料:
静态和动态绑定解释
构造函数调用顺序
Further references and worthwhile reading materials:
Static and dynamic binding explained
Order of constructor calls
这篇关于使用继承和覆盖方法在Java中复杂输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文