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。然后程序引用Foo 类型变量的公共字段a。
这里的错误是认为被称为覆盖的同一种概念适用于类字段。但是对于字段来说没有这样的概念:公共类Bar 的字段a 不是覆盖类a 的公共字段Foo 但它做了所谓的隐藏。顾名思义,就是在Bar类的范围内,a会引用Bar自己的字段,与Foo的字段无关。 (JLS 8.4.8 - Inheritance, Overriding, and Hiding)
那么,当我们写f.a 时,我们指的是哪个a?回想一下,字段a 的解析是在编译时 使用对象f 的声明类型完成的,即Foo。结果,程序打印出“3”。
现在,让我们在Foo 类中添加一个addFive() 方法,并在Bar 类中覆盖它,就像在考试问题中一样。这里应用了多态性,因此调用f.addFive()不是使用编译时间而是使用对象f的运行时类型来解析的,即Bar,因此打印为'b'。
但还有一点我们必须明白:为什么增加了 5 个单位的字段a 仍然坚持值“3”? hiding 在这里玩耍。因为这是被调用的Bar类的方法,又因为在Bar类中,每一个a都引用了Bar的公共字段a,这实际上是递增的Bar字段.
1) 现在,一个附属问题:我们如何从main 方法访问Bar 的公共字段a?我们可以这样做:
System.out.println( ((Bar)f).a );
这会强制编译器将 f 的字段成员 a 解析为 Bar 的 a 字段。
这将在我们的示例中打印“b 13”。
2) 还有一个问题:我们如何在addFive() 类Bar 的方法中隐藏 以不引用Bar 的@ 987654368@ 字段,但到它的超类同名字段?只需在字段引用前添加 super 关键字即可:
public void addFive() {
super.a += 5;
System.out.print("b ");
}
这将在我们的示例中打印 'b 8'。
注意初始语句
public void addFive() {
this.a += 5;
System.out.print("b ");
}
可以细化为
public void addFive() {
a += 5;
System.out.print("b ");
}
因为当编译器解析a字段时,它会开始在方法addFive()内查找最近的封闭范围,并找到Bar类实例,无需显式使用@987654376 @。
但是,好吧,this 可能是考生解决这个考试问题的线索!