需要Java代码段输出说明
我的代码是:
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(fa); } }
输出:
b 3
请向我解释,为什么这个问题的输出是“b 3”而不是“b 13”,因为该方法被覆盖了?
你不能在Java中覆盖变量,因此你实际上有两个变量 – 一个在Foo
,一个在Bar
。 另一方面, addFive()
方法是多态的,因此它修改了Bar.a
( Bar.addFive()
,尽管f
静态类型是Foo
)。
但最后你访问fa
并且使用已知类型的f
(即Foo
在编译期间解析此引用。 因此Foo.a
从未被触及过。
Java中的BTW非最终变量永远不应该公开。
F
是Foo
类型的引用,并且变量不是多态的,因此fa
指的是来自Foo
的变量,即3
如何validation?
要测试这个,你可以从Foo
删除a
变量,它会给你编译时错误
注意:将成员变量设为private
并使用访问者访问它们
另见
- 为什么要使用getter和setter?
有了这样一个问题,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(fa); } }
现在情况有点不那么令人困惑了。 main
方法声明一个Foo
类型的变量,在运行时为其分配一个Bar
类型的对象。 这是可能的,因为Bar
inheritance自Foo
。然后程序引用Foo
类型变量的公共字段a
。
这里的错误是相信称为覆盖的同类概念适用于类字段。 但是对于字段没有这样的概念:类Bar
的公共字段a
不会覆盖 Foo
类的公共字段a
,但它会执行所谓的隐藏 。 顾名思义,它意味着在Bar
类的范围内, a
将引用Bar
自己的字段,它与Foo
的字段无关。 ( JLS 8.4.8 – inheritance,覆盖和隐藏 )
那么,当我们写fa
,我们指a
是哪a
? 回想一下,字段a
解析是在编译时使用对象f
的声明类型完成的,即Foo
。 结果,程序打印’3’。
现在,让我们在类Foo
添加一个addFive()
方法,并在类Bar
覆盖它,就像在考试问题中一样。 这里多态性适用,因此调用f.addFive()
不使用编译时间而是使用对象f
的运行时类型(即Bar
f.addFive()
来解析,因此打印为“b”。
但是仍然有一些我们必须理解的东西:为什么增加5个单位的场a
仍然坚持价值’3’? 这里隐藏着玩耍。 因为这是被调用的类Bar
的方法,并且因为在类Bar
,每个a
引用了Bar
的公共字段a
,这实际上是增加的Bar
字段。
1)现在,一个附属问题:我们怎么能从main
方法进入Bar
的公共领域? 我们可以这样做:
System.out.println( ((Bar)f).a );
这迫使编译器解析f
的字段成员a
作为Bar
a
字段。
这将在我们的示例中打印’b 13′ 。
2)还有一个问题:我们怎样才能隐藏在类Bar
addFive()
方法中,而不是指向Bar
的a
字段而是指向它的超类eponimous字段? 只需在字段引用前添加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
类实例,从而无需明确地使用this
。
但是, this
可能是考生解决这个考试问题的线索!
既然你正在做fa
你将从类Foo
获得a的值。 如果你已经调用了一个方法来获取值,例如getA()
那么你就可以从类Bar
获得值。