考虑以下代码
class A
{
int x = 5;
void foo()
{
System.out.println(this.x);
}
}
class B extends A
{
int x = 6;
// some extra stuff
}
class C
{
public static void main(String args[])
{
B b = new B();
System.out.println(b.x);
System.out.println(((A)b).x);
b.foo();
}
}
程序的输出为
6
5
5
我理解前两个,但无法理解最后一个。 b.foo()如何打印5. B类将继承foo方法。但是,它不应该打印出什么b.x可以打印吗?到底发生了什么事?
答案 0 :(得分:18)
是的,B
类继承了foo
方法。但是x
中的变量B
隐藏了x
中的A
;它不会替代它。
这是范围问题。 foo
中的A
方法仅查看范围内的变量。作用域中唯一的变量是x
中的实例变量A
。
foo
在B
中被继承但未被覆盖。如果要使用相同的确切代码显式覆盖foo
:
class B extends A
{
int x = 6;
@Override
void foo()
{
System.out.println(this.x);
}
}
然后,this.x
所指范围内的变量将是B
的{{1}},并且将打印x
。尽管方法的文本相同,但是由于范围的原因,引用也有所不同。
顺便说一句,如果您确实想在6
类中引用A
的{{1}},则可以使用x
。
答案 1 :(得分:2)
在Java和具有与父类相同的字段名称的子类中,字段是不可覆盖的,“仅”遮蔽了父类的字段。
所以B
指的是当前类super.x
中定义的this.x
。
结果:x
。
更精确地说:A
方法是由5
子类继承的,但这并不意味着继承的方法的行为将因引用的实例字段而改变,因为所说的实例字段不是覆盖的:引用foo()
方法中的B
字段的this.x
表达式继续引用A.x
。
与前两个语句完全相同:
foo()
rgettman的很好回答说明了如何克服隐藏在子类中的字段。
解决隐藏问题的另一种方法是制作实例字段A.x
(推荐),并提供一种返回值的方法。
这样,您将从覆盖机制中受益,对于类的客户端,字段隐藏不再是问题:
B b = new B();
System.out.println(b.x); // refers B.x -> 6
System.out.println(((A)b).x); // refers A.x -> 5
b.foo(); // refers under the hood A.x -> 5
答案 2 :(得分:2)
好吧,这是因为静态绑定。
1)Java中的静态绑定发生在编译期间,而动态时发生 绑定在运行时发生。
2)私有方法,最终方法以及静态方法和变量 使用静态绑定并由编译器绑定,而虚拟方法是 在运行时根据运行时对象进行绑定。
3)静态绑定使用Type(Java中的Class)信息进行绑定 而动态绑定使用对象来解析绑定。
4)重载的方法在覆盖时使用静态绑定进行绑定 在运行时使用动态绑定来绑定方法。
答案 3 :(得分:1)
在 JAVA 中,方法可以被覆盖而变量不能。因此,由于您的方法foo
没有在B
中被覆盖,因此它将使用A
中的成员变量。
答案 4 :(得分:0)
致电时
b.foo();
它检查B
是否已覆盖方法foo()
,但尚未覆盖。然后,它向上一级查找到超类A
并调用该方法。
然后您调用了A
的{{1}}版本,然后将其打印出来
foo()
现在,this.x
无法看到A
的{{1}}版本。
为了解决此问题,您必须覆盖B
x
现在,打电话
B
将调用class B extends A
{
int x = 6;
@Override
void foo()
{
System.out.println(this.x);
}
}
的{{1}}版本,您将获得预期的结果。