我的代码是:
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”,因为该方法已被覆盖?
答案 0 :(得分:19)
F
是Foo
类型的引用,变量不是多态的,因此f.a
是指来自Foo
的变量3
如何验证?
要对此进行测试,您可以从a
中删除Foo
变量,它会为您提供编译时错误
注意:制作成员变量private
并使用访问者来访问它们
另见
答案 1 :(得分:16)
你无法覆盖Java中的变量,因此你实际上有两个a
个变量 - 一个在Foo
中,一个在Bar
中。另一方面,addFive()
方法是多态的,因此它会修改Bar.a
(Bar.addFive()
,尽管f
的静态类型为Foo
)。
但最后您访问f.a
并在编译期间使用已知类型的f
解析此引用,Foo
。Foo.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)还有一个问题:我们如何在a
类addFive()
方法中隐藏隐藏以引用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
中的值。