说明此Java代码中变量隐藏的工作方式

时间:2018-06-29 17:23:00

标签: java oop inheritance

考虑以下代码

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可以打印吗?到底发生了什么事?

5 个答案:

答案 0 :(得分:18)

是的,B类继承了foo方法。但是x中的变量B隐藏了x中的A;它不会替代它。

这是范围问题。 foo中的A方法仅查看范围内的变量。作用域中唯一的变量是x中的实例变量A

fooB中被继承但未被覆盖。如果要使用相同的确切代码显式覆盖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}}版本,您将获得预期的结果。