我正在检查一些Java练习,但我对这个练习感到困惑:
我们有一个具有这种结构的Foo类:
public class Foo {
public int a = 3;
public void addFive() {
a += 5;
}
}
继承自Foo的Bar类:
public class Bar extends Foo {
public int a = 8;
public void addFive() {
a += 5;
}
}
还有一个测试类:
public class Test {
public static void main(String[] args) {
Foo f = new Bar();
f.addFive();
System.out.println(f.a);
}
}
我认为输出是13,但它是3,我的问题是为什么?..
答案 0 :(得分:7)
添加到Peter的答案:请记住成员变量are not
是多态的,因此它们不是overriden
。
即f.a
仍然提供超级类a
的引用(因为f
的声明类型为Foo
)而f.addFive()
调用Bar
上的方法, (因为f
的运行时类型为Bar
);
所以,例如
Bar b = new Bar();
Foo f = b;
f.addFive();
System.out.println(f.a); // prints 3
System.out.println(b.a); // prints 13 as you have expected
希望很清楚。
答案 1 :(得分:5)
您有两个字段,一个隐藏,而不是替换另一个。您有Foo.a
和Bar.a
您覆盖的方法会改变Bar.a
,但您正在查找Foo.a
答案 2 :(得分:5)
你需要知道在Java late-binding(在运行时基于实际对象找到方法体的多态机制)仅对方法工作,而不是对字段工作。
因此,如果你有基类型Foo
的引用,它包含从Foo派生的类型的对象,就像在我们的案例中Bar
Foo f = new Bar();
然后使用f.field
,您将使用Foo
类中的字段(此处不存在多态行为)。
但是如果你使用f.someMethod()
,那么感谢运行时的后期绑定,Java将从Bar
类中找到并执行此方法的代码(因为f
拥有Bar
对象的内容) 。
所以在你的代码中
Foo f = new Bar();//you are creating reference of type Foo to instance of Bar
f.addFive();//body of this method will increment `a` declared in `Bar`
System.out.println(f.a);//here you are using `a` declared in Foo.
如果您使用a
类型的引用,则可以从Bar
类获取值Bar
。
System.out.println(((Bar)f).a);//prints 13.
答案 3 :(得分:0)
因为您正在将Bar对象初始化为Foo。这里有两个选择,要么将其初始化为直接Bar对象,要么将getter添加到直接连接到该属性的Bar对象。
Bar bar = new Bar();
bar.a;
或
public class Bar extends Foo{
public int a = 8;
public int getA() {
return a;
}
public void addFive(){
int a += 5;
}
}
bar.getA();
答案 4 :(得分:0)
在Bar#addFive()
方法中,您声明了本地int a
。对此本地int a
的更改不会影响班级成员int a
。由于f
是Foo
,因此a
的值在这种情况下为3
。
此外,您无需在addFive()
课程中重新声明Bar
方法,因为与Foo#addFive()
方法没有区别。
编辑:奇怪的是,您最初发布的代码已更改,int a
现在不再是本地代码。您使用的实际代码是什么?