编译器对父子类同名变量和同名方法的处理
带有override的情况
先看个例子,猜猜会输出什么?
class Base {
int count = 2;
public void display() {
System.out.println("Base : " + this.count);
}
}
class Derived extends Base {
int count = 20;
@Override
public void display() {
System.out.println("Derived : " + this.count);
}
}
public class Main {
public static void main(String[] args) {
Base bd = new Derived();
System.out.println(bd.count);
bd.display();
Derived d = new Derived();
Base d2b = d;
System.out.println(d2b.count);
}
}
输出的结果为:
2
Derived : 20
2
Tip:
编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。
原因如下:
编译器在处理方法和成员变量时存在区别。在子类没有重写父类的方法时,编译器会将父类中的方法copy到子类中,如果子类复写,则无法copy。对于实例变量而言却不存在这样的现象,子类定义的同名变量无法覆盖父类的同名变量,就如同上面“2和20”的例子,两个都存储了,正是由于变量和方法之间的处理存在这样的区别,所以对于一个引用类型的变量而言:
- 访问变量时,按照声明该变量时的类型(编译时类型)
- 访问方法时,按照实际引用的对象的类型(运行时类型)
因此,对于System.out.println(bd.count);
而言,bd的编译时类型为Base,因此就会输出Base的count,也就是2;对于bd.display();
而言,调用的是方法,bd的运行时类型为Derived,因此也就会调用了子类的display()方法。System.out.println(d2b.count);
同理,会输出编译时类型Base的count了
不带有override的情况
看完了上面的例子,别跟这个情况搞混了,看看下面代码,猜猜输出的是啥
class Father {
public void foo(Object o) {
System.out.println("Father.foo()");
}
}
class Son extends Father{
public void foo(String s) {
System.out.println("Son.foo()");
}
}
public class Main {
public static void main(String[] args) {
Son son = new Son();
son.foo(new Object());
}
}
别说这个也是“Son.foo()”,注意这个是overload!不同不同于上面的例子,son从父类中继承到了foo(Object o)方法,son会根据传入的参数不同而决定使用哪个方法。对于本例,传入的是Object,因此son会使用继承的foo方法,而非自己的。因此它的正确输出如下:
Father.foo()
_