需要Java代码段输出说明

Him*_*wal 17 java polymorphism inheritance

我的代码是:

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

输出:

b 3
Run Code Online (Sandbox Code Playgroud)

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

Jig*_*shi 19

F是类型的引用Foo,和变量不是多态的,从而f.a指的是从可变Foo这是3

如何验证?

要测试这个你可以从中删除a变量 Foo,它会给你编译时错误

注意:创建成员变量private并使用访问者访问它们


另见

  • 为了扩展这个答案,这就是为什么使用getter而不是直接访问变量通常更安全的原因之一. (10认同)
  • 如果在Java中使用通用编程实践,则可以悄悄地避免此问题:变量必须是私有的(只有自己的类可以使用它).之后,如上所述,使用getter和setter方法来访问/修改它. (2认同)
  • Dave说要创建私有字段并使用`getA()``setA()`访问器方法来访问字段 (2认同)

Tom*_*icz 16

你不能在Java中覆盖变量,因此你实际上有两个a变量 - 一个在Foo和一个Bar.另一方面,addFive()方法是多态的,因此它会修改Bar.a(Bar.addFive()尽管是静态类型的f存在,也会被调用Foo).

但最终你访问f.a并在编译期间使用已知类型解析此引用f,即Foo.因此Foo.a从未被触及过.

Java中的BTW非最终变量永远不应该公开.

  • 好吧,除非你有'GridBagConstraints`或类似的对象.但对于初学者来说,**从不**是一个合适的建议:) (4认同)

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

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

这里的错误是相信被称为重写的同类概念适用于类字段.但是对于字段没有这样的概念:a类的公共字段Bar不会覆盖类的公共字段a,Foo但它会执行所谓的隐藏.顾名思义,它意味着在类的范围内Bar,a将引用与Bar自己的领域无关的领域Foo.(JLS 8.4.8 - 继承,覆盖和隐藏)

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

现在,让我们addFive()在课堂上添加一个方法,Foo并在课堂上覆盖它,Bar就像在考试问题中一样.这里多态性适用,因此该呼叫f.addFive()正在使用不编译时间,但运行时类型对象的解决f,这是Bar,因此被印刷"B".

但是仍然有一些我们必须理解的东西:为什么a增加5个单位的领域仍然坚持价值'3'?这里隐藏着玩耍.因为这是Bar被调用的类的方法,并且因为在类中Bar,每个都a引用了Bar公共字段a,所以这实际上Bar是递增的字段.

1)现在,一个附属问题:我们如何从方法中访问Bar公共领域?我们可以这样做:amain

System.out.println( ((Bar)f).a );
Run Code Online (Sandbox Code Playgroud)

这迫使编译器来解决字段成员af作为Bara领域.

这将在我们的示例中打印'b 13'.

2)另一个问题:我们如何能够解决隐藏addFive()类的方法Bar来不是指Bara领域,但其超eponimous场?只需super在字段引用前添加关键字即可:

public void addFive() {
  super.a += 5;
  System.out.print("b ");
}
Run Code Online (Sandbox Code Playgroud)

这将在我们的示例中打印'b 8'.

注意初始声明

public void addFive() {
  this.a += 5;
  System.out.print("b ");
}
Run Code Online (Sandbox Code Playgroud)

可以提炼到

public void addFive() {
  a += 5;
  System.out.print("b ");
}
Run Code Online (Sandbox Code Playgroud)

因为当编译器解析字段时a,它将开始查看方法中最近的封闭范围addFive(),并找到Bar类实例,从而无需明确使用this.

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