2015-11-02 127 views
5
class A {int x = 5;} 
class B extends A {int x = 10;} 
class D { 
    public static void main(String[] args){ 
     A b0 = new B(); 
     System.out.print(b0.x); 
    } 
} 

我很奇怪,为什么这个代码打印的5而不是10静态实例变量查找的Java

如果我不是写以下,将变量x的方法,它的工作原理更是我所期待,并打印出10,因为在编译时只检查b0的静态类型A是否有方法x,然后在运行时使用b0的动态类型B来运行x。

class A {int x() {return 5;}} 
class B extends A {int x() {return 10;}} 
class D { 
    public static void main(String[] args){ 
     A b0 = new B(); 
     System.out.print(b0.x()); 
    } 
} 

我的理论是,实例变量静态查找不像方法,但我不知道为什么会这样。

谢谢!

回答

4

BA的字段x被覆盖(隐藏)未覆盖。回答“为什么会这样”参考文档herehere。编译器将根据包含对象的类型从x中选择两个实例中的一个。而b0的类型为A

A b0 = new B(); 

当你定义在另一方面(吸气),这些方法与父类相同的签名可以覆盖方法。另一个令人不愉快的惊喜是,父类中的字段即使是不同类型也会被遮蔽。

成员的阴影被认为是一种不好的做法,因为它容易让开发人员感到困惑。

4

因为您正在访问类A中的变量x,因为b0被定义为A。它被称为隐藏一个变量,而不是你可能会怀疑覆盖一个变量,这在java中是不可能的。如果您使用typeCast从b0访问x,则会得到预期的结果。

A b0 = new B(); 
System.out.print(((B)b0).x); 

通过使用类型转换,你会从类B访问变量x现在。

了解更多信息,你可以通过阅读JLS 8.3

3

静态字段不能被继承,它们不会覆盖对方,他们的影子对方。当你在你的案例中直接访问同名的静态字段时,超类的字段隐藏了另一个字段,并且你有两个int Xes,但超类中的一个不会隐藏并被选中。更好的是,当你调用同一个方法的另一个实例并访问相同的静态字段时,也就是当事情变得非常奇怪时。静态字段被加在一起,并且最终可能以X为5 + 5 = 10结束。

另一方面,如果从超类继承非静态字段,则不存在子字段值不同的问题,类,因为子类可以覆盖非静态超级成员。

静态变量和继承是不好的,它打破了你最不期待它的多态性。 (其实,如果你理解你的语言的概念,你期待它,但其他人可能不会)