当基类和派生类都具有相同名称的变量时会发生什么

考虑这些类中的
int a
变量:
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 test {
    public static void main(String [] args){
        Foo f = new Bar();
        f.addFive();
        System.out.println(f.a);
    }
}
我知道方法
addFive()
已在子类中被重写,并且在类测试中,当引用子类的基类引用用于调用重写方法时,调用子类版本
addFive
。 但是公共实例变量ѭ4怎么样?当基类和派生类具有相同的变量时会发生什么? 上述程序的输出是
b 3 
这是怎么发生的?     
已邀请:
实际上有两个不同的公共实例变量叫做
a
。 Foo对象有一个
Foo.a
变量。 Bar对象具有
Foo.a
Bar.a
变量。 当你运行这个:
    Foo f = new Bar();
    f.addFive();
    System.out.println(f.a);
addFive
方法更新
Bar.a
变量,然后读取
Foo.a
变量。要阅读
Bar.a
变量,您需要这样做:
    System.out.println(((Bar) f).a);
这里发生的事情的技术术语是“隐藏”。有关示例,请参阅JLS第8.3节和第8.3.3.2节。 请注意,隐藏也适用于具有相同签名的
static
方法。 但是,具有相同签名的实例方法被“覆盖”而不是“隐藏”,并且您无法访问从外部覆盖的方法的版本。 (在覆盖方法的类中,可以使用
super
调用重写的方法。但是,这是唯一允许这样做的情况。通常禁止访问重写方法的原因是它会破坏数据抽象。) 避免混淆(意外)隐藏的推荐方法是将实例变量声明为
private
,并通过getter和setter方法访问它们。使用getter和setter还有很多其他好的理由。     
来自JLS   8.3.3.2示例:隐藏实例变量此示例类似于   在上一节中,但使用   实例变量而不是静态   变量。代码:
class Point {
  int x = 2;
}
class Test extends Point {
  double x = 4.7;
  void printBoth() {
      System.out.println(x + " " + super.x);
  }
  public static void main(String[] args) {
      Test sample = new Test();
      sample.printBoth();
      System.out.println(sample.x + " " + 
                                              ((Point)sample).x);
  }
}
     产生输出:
4.7 2
4.7 2
     因为在类中声明了x   测试隐藏了x的定义   class Point,所以类Test没有   从它继承字段x   超类点。必须注意的是,   然而,那场x的场   class Point不是由类继承的   测试,它仍然实施   通过类Test的实例。其他   单词,Test的每个实例   包含两个字段,一个是int类型   和双重类型之一。这两个领域   承担名称x,但在   类测试声明,简单   name x总是指字段   在Test类中声明。代码   类Test的实例方法可以   引用实例变量x   class Point as super.x。      使用字段访问的代码   表达式访问字段x将   访问类中名为x的字段   由参考类型表示   表达。因此,表达   sample.x访问一个double值,即   在类中声明的实例变量   测试,因为变量的类型   样本是Test,但是表达式   ((点)样本).x访问一个int   value,声明的实例变量   在类Point中,因为强制转换为   键入点。     
在继承中,Base类对象可以引用Derived类的实例。 所以这就是
Foo f = new Bar();
的工作原理。 现在,当调用
f.addFive();
语句时,它实际上使用Base类的引用变量调用Derived类实例的'addFive()方法。所以最终调用'Bar'类的方法。但正如你所看到的'Bar'类的
addFive()
方法只是打印'b'而不是'a'的值。 下一个语句,即
System.out.println(f.a)
是实际打印最终被附加到前一输出的a的值,因此您将最终输出视为'b 3'。这里使用的值是'Foo'类的值。 希望这个技巧执行&编码很清楚,你明白你如何得到'b 3'的输出。     
这里的F是Foo类型,f变量是持有Bar对象但java运行时从类Foo获取f.a。这是因为在Java中,变量名称是使用引用类型而不是它引用的对象来解析的。     

要回复问题请先登录注册