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并使用访问者访问它们
另见
Tom*_*icz 16
你不能在Java中覆盖变量,因此你实际上有两个a变量 - 一个在Foo和一个Bar.另一方面,addFive()方法是多态的,因此它会修改Bar.a(Bar.addFive()尽管是静态类型的f存在,也会被调用Foo).
但最终你访问f.a并在编译期间使用已知类型解析此引用f,即Foo.因此Foo.a从未被触及过.
Java中的BTW非最终变量永远不应该公开.
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)
这迫使编译器来解决字段成员a的f作为Bar的a领域.
这将在我们的示例中打印'b 13'.
2)另一个问题:我们如何能够解决隐藏在addFive()类的方法Bar来不是指Bar的a领域,但其超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这可能是考生解决这个考试问题的线索!