需要Java代码段输出说明

时间:2012-08-15 17:51:17

标签: java inheritance polymorphism

我的代码是:

class Foo {
  public int a=3;
  public void addFive() {
    a+=5;
    System.out.print("f ");
  }
}

class Bar extends Foo {
  public int a=8;
  public void addFive() {
    this.a += 5;
    System.out.print("b ");
  }
}

public class TestClass {
  public static void main(String[]args) {
    Foo f = new Bar();
    f.addFive();
    System.out.println(f.a);
  }
}

输出:

b 3

请向我解释一下,为什么这个问题的输出是“b 3”而不是“b 13”,因为该方法已被覆盖?

4 个答案:

答案 0 :(得分:19)

FFoo类型的引用,变量不是多态的,因此f.a是指来自Foo的变量3

如何验证?

要对此进行测试,您可以从a中删除Foo变量,它会为您提供编译时错误

注意:制作成员变量private并使用访问者来访问它们


另见

答案 1 :(得分:16)

你无法覆盖Java中的变量,因此你实际上有两个a个变量 - 一个在Foo中,一个在Bar中。另一方面,addFive()方法是多态的,因此它会修改Bar.aBar.addFive(),尽管f的静态类型为Foo)。

但最后您访问f.a并在编译期间使用已知类型的f解析此引用,FooFoo.a。因此{{1}}从未被触及过。

Java中的BTW非最终变量从不是公开的。

答案 2 :(得分:12)

有了这样一个问题,SCJP考试正在评估你对所谓隐藏的了解。审查员故意使事情复杂化,试图让你相信程序的行为只取决于多态性,但事实并非如此。

在我们删除addFive()方法时,让我们尝试使事情更清晰一些。

class Foo {
  public int a = 3;
}

class Bar extends Foo {
  public int a = 8;
}

public class TestClass {
  public static void main(String[]args) {
    Foo f = new Bar();
    System.out.println(f.a);
  }
}

现在情况有点不那么令人困惑了。 main方法声明了Foo类型的变量,在运行时为其分配了类型为Bar的对象。这是可能的,因为Bar继承自Foo。然后程序引用类型为a的变量的公共字段Foo

这里的错误是相信称为覆盖的同类概念适用于类字段。但是对于字段没有这样的概念:公众类a的字段Bar不会覆盖a的公共字段Foo,但它会执行所谓的隐藏。顾名思义,这意味着在类Bar的范围内,a将引用Bar自己的字段,该字段与Foo无关。一。 (JLS 8.4.8 - Inheritance, Overriding, and Hiding

那么,当我们写f.a时,我们指的是a?回想一下,使用对象a的声明类型f,在编译时完成字段Foo的解析。结果,程序打印'3'。

现在,我们可以在课程addFive()中添加Foo方法,并在课程Bar中覆盖它,如同考试问题一样。这里多态性适用,因此调用f.addFive()不是使用编译时间而是使用f的运行时类型Bar来解析,因此打印为“b”。

但仍然有一些我们必须理解的事情:为什么增加5个单位的字段a仍然坚持值'3'?这里隐藏正在玩耍。因为这是调用类Bar的方法,因为在类Bar中,每个a都引用Bar的公共字段a,实际上是递增的Bar字段。

1)现在,一个附属问题:我们如何从Bar方法访问a的公共字段main?我们可以这样做:

System.out.println( ((Bar)f).a );

强制编译器将a的字段成员f解析为Bar的{​​{1}}字段。

这会在我们的示例中打印'b 13'。

2)还有一个问题:我们如何在aaddFive()方法中隐藏隐藏以引用Bar 1}} Bar字段,但它的超类eponimous字段?只需在字段引用前添加a关键字即可:

super

这会在我们的示例中打印'b 8'。

请注意初始陈述

public void addFive() {
  super.a += 5;
  System.out.print("b ");
}

可以改进为

public void addFive() {
  this.a += 5;
  System.out.print("b ");
}

因为当编译器解析字段public void addFive() { a += 5; System.out.print("b "); } 时,它将开始查找方法a中最近的封闭范围,并找到addFive()类实例,从而消除需要明确使用Bar

但是,this可能是考生解决这个考试题的线索!

答案 3 :(得分:1)

由于您正在执行f.a,因此您将从班级a获得Foo的值。如果您调用了一个获取值的方法,例如getA(),那么您将获得类Bar中的值。