当前位置: 首页 > 知识库问答 >
问题:

用继承和重写方法困扰Java输出

鲜于宏义
2023-03-14

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

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,为什么会出现在B之后?

共有1个答案

法镜
2023-03-14

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

  • 根据引用类型解析字段引用,并根据对象类型在运行时(以动态方式)解析方法调用。
  • super()隐式地放置在每个构造函数中,即使您自己不把它放在那里(例如,如果调用super(int x,int y),则不会调用它)。
  • 从构造函数调用“可重写”的方法被认为是非常糟糕的做法--在执行过程中您将看到原因。

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

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

我们完蛋了!:)

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

 类似资料:
  • 问题内容: 我偶然发现了这段代码。 我试图在实际运行之前猜测运行它的结果。看到他们时,我真的很困惑,需要一些解释。 这是代码: 输出为: 这是为什么? 怎么样 为什么会出现?我还没有实例化。 如果出现,为什么 之后 呢? 问题答案: 在我开始解释代码执行的每一步之前,您应该了解一些事实: 根据引用类型解析字段引用,并在运行时基于对象类型解析方法调用(以动态方式)。 即使您自己没有将其 隐式 放置在

  • “编写一个名为clsWorker的超类和子类clsHourlyWorker和clssalariedworker。每个工人都有一个名字和一个工资率。编写计算每个员工周薪的方法computePay(int hours)。小时工按实际工作小时数获得小时工资,如果小时数最多为40小时。如果小时工工作超过40小时,则按时间半支付超出部分。受薪工人得到40小时的小时工资,无论实际小时数是多少。为继承编写一个测

  • 如何/可以重写来自非继承类的方法?其次,有没有比“非继承类”更好的术语? 我有一个“扩展”JFrame的类,需要从JPanel重写paintComponent。怎么做?或者它可以扩展JPanel,并需要访问方法,如setTitle()、setResizable()和setDefaultCloseOperation();

  • 问题内容: 我有一个抽象的Parent类,其中有多个孩子。我希望孩子能够拥有一个对该孩子的每个实例都相同的变量。我不希望将构造函数传递给孩子来告诉它它的名字,因为在可以对其进行硬编码时,这似乎很愚蠢。从我读到的内容来看,以下“隐藏”了parents实例变量,并且无法按我的意愿工作。 明确地说,基本上我想要的是类似c.getClass()。getName()的东西,但是我不想让结果依赖于类名,而是依

  • 问题内容: 这是我遇到的一个测试练习问题,希望您能帮助我理解概念 让Hawk成为Bird的子类。假设某个类有两个重载的方法void foo(Hawk h)和void foo(Bird b)。在声明Bird x = new Hawk()之后,将在调用foo(x)中执行哪个版本; 这是我到目前为止的代码,有人可以向我解释为什么foo(bird b)被执行吗? 问题答案: Java执行重载解析以选择方法

  • 问题内容: 我有两个课,和。它们看起来像这样: 此错误指向Field的: 我希望首先调用Background init ()。要将“ a,b”传递给Fields的 init (),Field会分配a和b,然后将其中包含三个0的列表分配给field。然后让Background的 init ()继续,然后调用它自己的buildField()并用包含c的列表覆盖self.field。 似乎我还没有完全理