使用继承和覆盖方法在Java中复杂输出 [英] Perplexing output in Java with inheritance and overriding methods

查看:132
本文介绍了使用继承和覆盖方法在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 instantiated A at all.
  • And if A appears, why is it after B?

推荐答案

在我开始解释代码执行的每一步之前,你应该知道几个事实:

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 call super(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 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 constructor B().
  • As I said before, the call to super() is added implicitly to any constructor, so A() is immediately called.
  • Inside A() you call foo(), which is overridden in class B and that is why foo() is called from B.
  • Inside B's foo() you get the output B.foo(): bar = null since Java didn't get to initialize B's fields yet (its constructor hasn't been executed yet!) and the fields of object type are initialized to null by default.
  • Now that we are done with the constructor of A() we go back to the constructor of B().
  • Inside said constructor, we have the call to foo() again, which is again B's foo(). But different from last time, B have its fields initialized (after the call to super()) properly so you get the expected B.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 field bar of A.
  • Lastly, for the same reason, you call a.foo() which again triggers B's foo() which prints b.bar once again.

我们完成了! :)

更多参考资料和值得阅读的资料:

静态和动态绑定解释

构造函数调用顺序

Further references and worthwhile reading materials:
Static and dynamic binding explained
Order of constructor calls

这篇关于使用继承和覆盖方法在Java中复杂输出的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆