多态的基础理解请参考:http://www.cnblogs.com/liujinhong/p/6003144.html
Java的多态一直是我们理解的一个难点。在读过《深入理解Java虚拟机》和《Java编程思想》以后,感觉理解更深刻了。
我们知道,当父类引用指向子类对象,并且子类覆盖父类中的接口时,当用父类引用调用此接口,则实际上是调用子类对象中的接口。
其实要理解这一点并不难,首先要明确下面两点:
1. 父类引用为何能指向子类对象?
子类是由父类继承而来,所以子类包含了父类中所有的接口。他们的关系是is-a关系,在任何使用父类的地方都可以用一个子类来代替,这也就是里氏替换原则。
里氏代换原则(Liskov Substitution Principle, LSP):所有引用基类(父类)的地方必须能透明地使用其子类的对象。
2. 对象的方法是如何被调用的?
在Java中将一个方法调用和一个方法主体关联起来被称作绑定。若能在程序执行前进行绑定,叫做前期绑定。如果在运行时根据对象的具体类型进行绑定就是后期绑定,也叫作动态绑定或者运行时绑定。
误区:以为只有实现多态是后期绑定,而不满足多态条件的方法调用是前期绑定。(这也是我之前的理解)
在Java中只有static和final修饰的方法是前期绑定,而其他所有方法都是后期绑定!也就是说后期绑定是自然而然发生的,对一个对象发送的消息必须是根据对象的实际类型来寻找该类中定义的方法。这是如何实现的呢?我的简单理解如下:在JVM中,我们通过一个引用来定位一个对象,而对象中同样也存储了该对象本身的类型信息(指向了方法区中的Class对象),这也就是我们可以用getClass()方法来获取对象的class的原因。那么对一个对象发送消息的时候,我们必然也能找到该对象的类型信息,然后再找到该类中合适的方法。
3. 多态的缺陷。私有方法,域和静态方法不适用于多态。
a. 不能覆盖基类中的私有方法
由于private方法自动认为是final的,而且对子类是屏蔽。
子类中的print()方法是一个全新的方法,而没有覆盖父类中的方法。
b. 不能覆盖域与静态方法
output:
father
I am:son
son
I am:son
I am:father
只有普通方法的调用是多态的,如果直接访问某个域,这个访问就是在编译期间进行解析的,因此不是多态的。在上例中,为Father.name和Son.name分配了不同的存储空间。这样Son实际上包含两个称为name的域:它自己的和它从父类中得到的。
如果某个方法是静态的,它的行为就不具有多态性。
output:
father
father static
son
son static
静态方法是与类而不是单个对象相关联的。