日期:2014-05-20  浏览次数:20731 次

父类引用指向子类对象的问题
FatherClass f = new SonClass()


当父类引用f指向其子类的对象的时候,通过f无法访问专属于子类对象的成员。

假如子类中有对父类方法的重写,那么根据多态机制,通过f访问这个方法的时候实际访问的是子类中重写的方法。

问题是如果子类重写的方法中访问了专属于子类的成员变量,这时候通过父类引用f还可以调用那个被重写的方法吗?

------解决方案--------------------
当父类引用f指向其子类的对象的时候,通过f无法访问专属于子类对象的成员。
为什么这样不可以?LZ有没有想过,因为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指向子类对象,所以会解析子类重写的方法代码指令,而子类对象的内存空间是包含子类的成员变量的空间的,所以也不存在子类成员变量没有分配内存的问题,所以可以调用。

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

------解决方案--------------------
其实多态也挺好理解的,你也提到父类引用指向子类对象,f只是一个引用,它指向的才是我们实际访问的对象,f只是我们访问对象的一个入口,这个入口是父类类型,只提供了父类暴露出来的方法,所以我们在通过f访问时只能执行父类暴露出来的方法,但我们实际访问的是子类对象,至于子类是怎样实现这方法的和父类完全没有关系,子类完全可以调用自己的任何方法和属性。像一个类实现一个接口一样,接口方法都没有实现,我们同样可以用接口的引用指向其实现类的对象。
------解决方案--------------------
其实简单的一点的想,如果子类重写的父类方法,不能处理子类里一些自己的属性内容,那个我们所谓的多态还有意义没呢?而且这样的话我们继承重写的时候是不是回要设定一个规则,就是重写的方法还要限制不能使用子类特有的属性和方法呢?
如果这样的话,继承,重写,动态绑定还有什么意思呢?
------解决方案--------------------
首先是可以的因为我们现在基本都是采用的是接口加实现类的编程方式,一般接口中不定义变量,变量都是在子类中定义的,但是我们仍然能够通过接口调用方法来实现调用子类的方法。
为什么会这样的,就我个人理解,一个引用其实就是一个表示地址的“字符串”,那么一般我们通过引用方法方法都是在通过这个地址来找,这个地址里所对应的对象,然后调用该对象的方法,所以既然你引用的实际地址是子类对象的地址,那么这个时候通过调用引用的方法不就是在调用子类的方法吗,自己调用自己的方法有什么不可以的,最后想总结一句话就是,调用引用的方法到底属于谁的方法只需要看引用表示的地址实际指向的对象   这些仅仅是个人对于多态的理解,个人感觉中间的理论还有问题但是对于使用上基本没什么问题