令人困惑的 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
出现了,为什么是afterB
?
- 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:
- 字段引用根据引用类型解析,方法调用在运行时(以动态方式)根据对象类型解析.
super()
被隐式放置在 every 构造函数中,即使你自己没有把它放在那里(它不会被调用,如果你例如调用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()
的调用被隐式添加到任何构造函数中,因此A()
会立即被调用. - 在
A()
内部调用foo()
,它在B
类中被覆盖,这就是为什么foo()
是从B
调用的. - 在
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
再次.
- 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屋!
查看全文