我们可以从对象的内存角度来理解试试:
假设现在有一个父类Father
,假设Father类里面的变量占内存2M
,有一个它的子类Son
,Son
里面的变量占用内存1M
。
Father f = new Father();//系统分配2M内存
Son s = new Son();//系统分配3M内存(2M+1M)
这里可能你会有疑问了,Son
的变量不是只占1M
内存吗?为什么系统给上面的s
分配了3M内存?因为子类中有一个隐藏的引用super
会指向父类实例,所以在实例化子类之前会先实例化一个父类,也就是说会先执行父类的构造方法,由于s
中包含了父类的实例,所以s
可以调用父类的方法。
下面我们来看一下代码:
public class Main {
public static void main(String[] args) {
Father f = new Son();
f.fun1();
}
}
class Father {
public void fatherFun(){
System.out.println("fatherFun");
}
public void fun1(){
System.out.println("fatherFun1");
}
}
class Son extends Father {
public void childFun(){
System.out.println("childFun");
}
public void fun1(){
System.out.println("childFun2");
}
}
执行Father f = new Son();
语句为向上转型。假设Father类里面的变量占内存2M
,有一个它的子类Son
,Son
里面的变量占用内存1M
。这时候f
会指向那3M
内存中的2M
,就是说,f
只是指向了子类实例中的父类实例对象。所以f
只能调用父类的方法(存储在2M
内存中),而不能调用子类的方法(存储在1M
内存中)。
在main
方法中,执行f.fun1();
语句。控制台会打印子类中重写父类的fun1()
方法中的内容"childFun2"
。
在main
方法中,执行f.fatherFun();
语句。控制台会打印父类中的fatherFun()
方法中的内容"fatherFun"
。
在main
方法中,执行f.childFun();
语句。会报编译错误,因为childFun
方法是子类有而父类没有的,f
不能调用。父类引用只能调用子类实例中重写父类的方法
。