一、后期绑定
对象能够向上转型,才能表现出多态。那么为什么可以向上转型呢?这里我们需要了解一下几个概念:
- 绑定: 将一个方法调用同一个方法的主体联系起来,称作“绑定”;
- 前期绑定:在程序执行之前进行绑定;
- 后期绑定:在程序运行时根据对象的类型进行绑定,我们也称“动态绑定”或者“后期绑定”(java中除了static方法和final方法之外,其中,private方法属于final方法,都是属于后期绑定的方法);
注意:final 可以“关闭”动态绑定,这样编译器不需要对其进行动态绑定,可以为final方法调用生成更有效的代码。但是,我们最好根据设计来决定是否使用final,而不是试图提高性能。
所以说,由于后期绑定这种机制的存在,我们在进行方法的调用时,可以直接只关注基类对象,在运行时,会找到真正的导出类的对象,并调用对象的方法。如此,我们能够顺利的完成向上转型,并使得导出类的方法能够得到正确的调用。此时,我们站在基类的角度来看,相同的方法对于不同的导出类而言,可以执行各自不同的任务,这样就表现出了多种形态既多态。
二、构造器调用顺序
但是,如何保证这种向上转型能够顺利完成呢?
我觉得那是因为我们在类初始化的时候有一套机制进行了保护,既当我们创建一个复杂类的时候,我们在进行构造时有指定的执行顺序。构造器调用顺序如下:
- 调用基类构造器(不断递归下去,从最底层的基类开始);
- 按声明顺序调用成员的初始化方法;
- 调用导出类构造器的主体;
这么做的目的是保证所有基类成员以及当前对象都被初始化了。
注意一:
当我们进行清理工作时,应该先对导出类进行清理,然后才是基类,因为导出类可能使用的基类的某些方法等,这时是和声明的顺序相反的。
注意二:
用尽可能简单的方法使对象进入正常状态,如果可以的话,避免调用其他方法。举例来说,如果B继承了A,如果我们在A的构造初始化过程中掉用了a方法,但是a方法不是final(包含了private)的,那么根据后期绑定的机制,就会调用导出类中a’方法,显然,这个时候a’方法覆盖了a方法,但是B类初始化还没有完成,也就是说某些成员变量的初始值为0或者null,在这种情况下,就很有可能报错啦。
三、多态的设计
- 1、继承与组合
在我们创建新的类的时候,我们应该优先选择“组合”来设计,然后以“继承”辅助,因为组合来说,直接可以知道某某的行为,但是就继承而言,你必须要知道该类的具体类型才能找到相应的行为。如果,我们在一个地方需要表现为不同的状态的时候,可以考虑通过继承来设计,达到相同的”调用“,表现出不同的行为,从而表现出了多态的优势。
- 2、纯继承与扩展
通过继承的关系我们可以从基类导出不同的子类,子类可以在基类的基础上扩展增加自有的方法等,这样我们可以轻松的拥有了基类的方法的同时还可以增加自己的方法。
- 3、向下转型与运行时类型识别
我们知道,导出类自有扩展的方法是基类没有的,所以,如果我们向上转型为基类的类型,那么该基类的导出类自有的方法就会无法调用,这个时候我们需要进行向下转型,还原成导出类的对象,在转型的过程中,是没有任何损失的。但是,我们向下转型是不安全的,因为导出类具有大于基类的接口,所以我们在运行会进行检测,如果有错误便会抛出异常。
小结
多态,意味着“不同的形式”。在面向对象的程序设计中,我们持有从基类继承而来的相同的接口,以及使用该接口的不同形式:不同版本的动态绑定方法。这里的接口,是我们必须要理解的。我们可以认为,每一个类的N个方法实际可以抽离出来,看成这个类实现了N个接口。从接口的角度来看待类的方法,是很有必要的。