【问题标题】:Perplexing output in Java with inheritance and overriding methods使用继承和覆盖方法在 Java 中产生令人困惑的输出
【发布时间】:2016-04-21 06:12:49
【问题描述】:

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

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

【问题讨论】:

    标签: java inheritance methods constructor overriding


    【解决方案1】:

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

    • 字段引用根据引用类型解析,方法调用在运行时(以动态方式)根据对象类型解析。
    • super()隐式 放置在 every 构造函数中,即使您自己没有将它放在那里(例如,如果您调用 super(int x, int y),它不会被调用)。李>
    • 从构造函数调用“可覆盖”方法被认为是非常糟糕的做法 - 当我们执行执行时,您会明白为什么。

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

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

    我们完成了! :)

    更多参考资料和值得阅读的材料:
    Static and dynamic binding explained
    Order of constructor calls

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-23
      • 1970-01-01
      • 1970-01-01
      • 2013-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多