首先看一个问题:

对于FatherClass  f  =  new   SonClass ();  

当父类引用f指向其子类的对象的时候,通过f无法访问专属于子类对象的成员。假如子类中有对父类方法的重写,那么根据多态机制,通过f访问这个方法的时候实际访问的是子类中重写的方法。问题是如果子类重写的方法中访问了专属于子类的成员变量,这时候通过父类引用f还可以调用那个被重写的方法吗?

回答:

当父类引用f指向其子类的对象的时候,通过f无法访问专属于子类对象的成员。
为什么这样不可以?因为f是FatherClass,所以编译器只知道f拥有FatherClass.class的信息,FatherClass.class以外的信息,编译器不知道,而子类的对象成员是在SonClass.class里,也就是说在FatherClass.class以外,所以f无法访问子类的对象成员。

假如子类中有对父类方法的重写,那么根据多态机制,通过f访问这个方法的时候实际访问的是子类中重写的方法。
为什么这样可以?上面说了,f只能访问FatherClass.class的信息(注意这里指的是编译期编译器只知道f是FatherClass类型,不知道f具体指向什么对象,运行期才知道指向什么对象),而子类重写的方法,父类中也存在,即SonClass.class重写的方法,FatherClass.class里也有(如果SonClass.class里有但是FatherClass.class里没有的方法,f也不能直接调用),所以f可以访问,但是调用的时候(注意这里指的是运行期),f实际指向的是SonClass对象,所以调用的是SonClass对象的方法。

问题是如果子类重写的方法中访问了专属于子类的成员变量,这时候通过父类引用f还可以调用那个被重写的方法吗?
可以,要分清编译期和运行期,编译期是编译器检查语法和类型,运行期是解析器解析伪代码为机器指令而执行,编译期编译器会检查f的访问范围,也就是f的访问不超过FatherClass.class的信息就不会出错,运行期解析器会解析方法的代码指令,因为f指向子类对象,所以会解析子类重写的方法代码指令,而子类对象的内存空间是包含子类的成员变量的空间的,所以也不存在子类成员变量没有分配内存的问题,所以可以调用。

要分清楚,为什么会生成FatherClass.class和SonClass.class两个文件,既然是生成两个文件,那当然是那个文件被使用,就可以访问那个文件的信息。编译期编译器只知道f是FatherClass类型(至于f=什么,那是运行期,因为不运行=就不会执行),所以会检查f是否在FatherClass.class的范围,而运行期,f指向的是SonClass对象,也就是内存的对象是SonClass.class的信息,所以执行的是SonClass.class的代码指令。


其实多态也挺好理解的,父类引用指向子类对象,f只是一个引用,它指向的才是我们实际访问的对象,f只是我们访问对象的一个入口,这个入口是父类类型,只提供了父类暴露出来的方法,所以我们在通过f访问时只能执行父类暴露出来的方法,但我们实际访问的是子类对象,至于子类是怎样实现这方法的和父类完全没有关系,子类完全可以调用自己的任何方法和属性。像一个类实现一个接口一样,接口方法都没有实现,我们同样可以用接口的引用指向其实现类的对象。

-------------------------------------------------------------------------------------------------------------------------------------------