public class TestFoo{
public static void main(String[] args) {
Fo f = new Bar();
f.addFive();
System.out.println(f.a);
//f.print();
//System.out.println(f.b);
//System.out.println(f.getA());
}
}
class Fo{
public int a;
public Fo(){
a = 3;
}
public void print(){
System.out.println("这里是父类!");
}
public void addFive(){
a += 5;
}
}
class Bar extends Fo{
public int a;
public int b;
public Bar(){
a = 8;
}
public void addFive(){
this.a += 5;
}
public int getA(){
return this.a;
}
}
代码的输出结果为3。
相信很多学到Java对象的读者会有这样的疑问:为什么我在子类中重写了方法,调用了重写方法,经过方法体“改造”所得到的成员变量输出结果却不是我想要的?
这里就涉及到了java面向对象程序设计的四大特征之一的多态性。
- 父类和子类的成员变量是同时存在的,即便甚至是同名。
- 在子类中看到的是子类的变量(也包括从父类继承的除私有之外的成员变量,虽然被隐藏了),在父类中看到的是父类中的变量。
- 各成员变量互不影响,而同名的成员方法则是实实在在的覆盖(重写)
在此例中 Fo f = new Bar(); 父类引用指向子类对象就是多态中的向上造型,父类引用只能调用父类的方法。
我们可以看到父类的方法可以被调用(包括父类特有的方法),但是子类特有的方法却无法调用。
而其中如果此方法被它指向的子类对象进行了重写,那么调用的方法就是子类重写后的形式,这也是多态的表现(动态绑定))。接下来我会利用DeBug将该问题和开篇所讲的成员变量一同述说。
可以看到先是调用父类的构造方法对成员变量进行初始化
随后是在子类的构造方法中对子类的成员变量进行初始化(注意这里的同名成员变量a是子类自己的)
到了这一步确实也是验证了上文所说的调用子类的同名重写方法,但注意这里同样也是对子类自己的同名成员变量a进行重赋值,而不是对父类的成员变量进行覆盖重写。
当方法退出方法栈后,返回主方法后,我们可以看到图中第一行和第三行有标注,f对象的引用地址都是同一地址,因为始终是父类引用。到这里结果自然而然显现出开头所说的输出结果为3.
访问同名成员变量和访问同名成员方法是不同, 因为java中,向上造型呈现的多态性仅仅针对成员方法,成员变量不具有多态性。
另外我们还可以看到,父类引用对于子类特有的成员变量也是不允许访问的。