所以我有以下3个类:

具有 1 个字段和 1 个调用方法的构造函数的类“A”:

public class A {
    String bar = "A.bar";

    A() {
        foo();
    }

    public void foo() {
        System.out.println("A.foo(): bar = " + bar);
    }
}

从 A 继承的第二个类“B”具有 1 个字段和 1 个调用方法的构造函数:
public class B extends A {
    String bar = "B.bar";

    B() {
        foo();
    }

    public void foo() {
        System.out.println("B.foo(): bar = " + bar);
    }
}

包含主要方法的第三类“C”:
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

我在 Debug模式下跟踪程序,但我仍然无法弄清楚输出。我希望详细解释所发生的过程及其背后的原则。
非常感谢。

编辑:就像人们指出的那样,我确实忘记了输出的最后一行:

B.foo(): bar = B.bar

最佳答案

  • 你调用B
  • 的构造函数
  • 在 B 的构造函数中,A 的构造函数被称为隐式
  • A 的构造函数调用方法 foo()
  • 由于方法 foo() 在 B 上被覆盖,Java 解析到这个显式方法并调用这个实现
    这里有 B.foo(): bar = null,因为对象尚未初始化
  • 然后 B 的构造函数继续执行并再次调用 foo(),那么你就有了 B.foo(): bar = B.bar
  • 构造函数完成其执行,因此您拥有的下一个字符串来自您的系统。在这里,因为您使用 A 类来访问您的实例,并且由于不能在子类上覆盖类的属性(至少不是那样)Java 将 bar 的值解析为存储在 A 中的值。

  • 这是有关此的文档

    子类属性仅隐藏父类(super class)属性。
    https://docs.oracle.com/javase/tutorial/java/IandI/hidevariables.html

    关于 super 的工作原理和 super 构造函数的一些说明
    https://docs.oracle.com/javase/tutorial/java/IandI/super.html

    不要在构造函数中调用可覆盖的方法
    https://www.securecoding.cert.org/confluence/display/java/MET05-J.+Ensure+that+constructors+do+not+call+overridable+methods

    附言您错过了您提供给我们的输出中的最后一个方法调用 hehe

    关于java构造函数行为继承和静态/动态绑定(bind),我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/31075040/

    10-14 12:30
    查看更多