使用继承和重写方法在Java中复杂输出

Ido*_*dos 2 java methods inheritance constructor overriding

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

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();
    }
}
Run Code Online (Sandbox Code Playgroud)

输出是:

B.foo(): bar = null
B.foo(): bar = B.bar
a.bar = A.bar
B.foo(): bar = B.bar
Run Code Online (Sandbox Code Playgroud)

这是为什么?

  • 怎么样bar = null
  • 为什么a.bar = A.bar甚至出现?我根本没有实例化A.
  • 如果A出现,为什么会之后 B

Ido*_*dos 7

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

  • 基于引用类型解析字段引用,并且基于对象类型在运行时(以动态方式)解析方法调用.
  • super()放置隐含每一个构造函数,即使你不把它存在自己(如果你调用它不叫super(int x, int y)为例).
  • 从构造函数中调用"可覆盖"方法被认为是非常糟糕的做法 - 当我们执行执行时,您将看到原因.

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

  • B通过调用其默认构造函数来实例化B().
  • 正如我之前所说,调用super()隐式添加到任何构造函数,因此A()立即调用.
  • A()你打电话里面foo(),在课堂上被覆盖B,这就是为什么foo()要从中调用B.
  • 里面Bfoo(),你得到的输出B.foo(): bar = null,因为Java的没有得到初始化B的领域,但(其构造尚未执行!)和对象类型的字段将初始化为null默认.
  • 现在我们已经完成了构造函数,A()我们回到构造函数B().
  • 里面说的构造函数,我们必须调用foo()一次,而这又是Bfoo().但与上次不同的是,B将其字段初始化(在调用之后super()),以便获得预期B.foo(): bar = B.bar.
  • 现在我们又回到了温暖的怀抱main.
  • 您访问a.bar,并因为正如我所说的字段引用是基于引用类型解决,你得到的场barA.
  • 最后,出于同样的原因,你叫a.foo()这再次触发Bfoo(),其打印b.bar一次.

我们完成了!:)

进一步的参考和值得阅读的材料:
静态和动态绑定解释
了构造函数调用的顺序