多态
多态
多态优点:增强了代码组织结构和可读性, 还能拓展功能,即生长程序。
多态:因为继承允许对象(子类以下)视为本身类型或者其基类型来处理,所以当同一份代码运行再不同类型上会产生不同的效果,这就叫作多态。
再论向上转型
这里,书中讨论了向上转型的重要性,如果不向向转型,参数中不是基类型而是准确无误的子类型,那么当我们需要其他子类型的同一方法的时候将一一去创建子类的,这样将会重复造轮子。
转机
方法调用绑定
关于为什么编译器能够准确得去定位,绑定子类的方法。
绑定:将一个方法调用同一个方法主体关联起来,被称为绑定。
ps: private修饰的方法,应该属于final方法。因为private不仅不能修改还不能访问。由此提出:除了static静态的方法和final方法外其他方法都是后期绑定的,也就是后期去关联它的方法实现的主体。顺便提一嘴, final使用虽然能提高微不足道的性能,但是最好从设计去考虑,该该方法是否要面向客户端程序员。
方法能调用正确的方法:
书中通过创建一个工厂, 工厂中通过switch,和随机数,里面放入子类的对象,根据随机数进行获取。
以上用案例证明, 编译器事先是不知道的,是后期绑定进行调用方法的。
可扩展性
大多数的方法都遵循这个多态的模型,只与基类接口通信。这样的程序是可拓展的
缺陷:覆盖私有方法
这里是指父类方法中修饰方法的访问控制为private的时候(private 会自动认为final类型不可改变),所以如果当使用多态模型的时候, 向上转型中是不存在这个方法的, 那么这个方法再新类中相当于是全新的方法和父类方法无关。
缺陷:域与静态方法
域(成员变量)
- 当 Super su = new Sub()
- 域名均为相同的话。
那么调用域的值将会是super父类的值。这是因为:sub对象转化为super引用的时候,任何访问操作都是由编译器去解析的。
在此我们要知道多态方法的解析:
方法调用并不等同于方法执行,方法调用阶段唯一的任务就是确定被调用方法的版本(即调用哪一个方法),暂时还不涉及方法内部的具体运行过程。在程序运行时,进行方法调用是最普遍、最频繁的操作,Class文件的编译过程中不包含传统编译中的连接步骤,一切方法调用在Class文件里面存储的都只是符号引用,而不是方法在实际运行时内存布局中的入口地址(相当于之前说的直接引用)。这个特性给Java带来了更强大的动态扩展能力,但也使得Java方法调用过程变得相对复杂起来,需要在类加载期间,甚至到运行期间才能确定目标方法的直接引用。
符号引用的作用是通过一段代码进行计算最终的入口地址。然后得出。然而这种错误很难出现, 因为我们不会让父类和子类的域名一致, 我们通常也对域是用private修饰,用get方法去获得。
构造器和多态
构造器的调用顺序
- 调用基类构造器
- 按声明顺序调用成员的初始化方法
- 调用导出类构造器的主体
继承和清理
Java一般由垃圾回收器进行清理, 所以我们不需要进行手动清理, 但是有时候需要我们马上进行清理操作, 那么我们要遵循一条原则进行清理。
假设我们有创建的第二个对象依赖于第一个对象(比如第二个对象会调用第一个对象中的一些方法),所以我们要声明逆序的方式进行清理。
构造器内部的多态方法
再构造器中的调用的方法且最好是final的,因为基类的构造器是比导出类的构造器和成员变量先进行调用的。
还有导出类进行覆盖基类的方法的时候, 导出类如果调用这个方法就会默认调用导出类的方法, 且如果导出类这个方法中有导出类中的成员变量, 那么这个成员变量将为默认值, 因为这个时候导出类的成员变量还未初始化。
这里因为引进了继承关系,所以重新理理他们的顺序:
- 在最开始会将分配给对象存储空间初始化成二进制的0,所以成员变量为默认值
- 递归从基类构造器往子类调用,(上面例子中就是在这个时候调用子类中被覆盖的方法所以,redius是为0)
- 按照声明顺序调用成员变量的初始化方法。也是遵从父类自顶向下的方案。
- 最后会到基类为止
最后顺便提一下:
代码块地初始化操作也会优先于构造器地初始化。
静态成员变量会优先非静态。以此可以按照他们的顺序和级别进行排序标记。
协变返回类型
协变函数的本质是, toString 方法能否也根据动态绑定实现多态, 而Java版本中se5之前的版本是不能的,se5之后是可以的
这里面根据我们照常的想法进行一步步的换算就可以的, 至于se5之前实现不了,是根Java编译设定有关,读者可以忽略这个协变返回类型
用继承进行设计
- 通过继承表达行为之间的差异。并设计出差异性的代码
- 通过一个方法运用组合使自己的章台发生改变
纯继承与扩展
导出类和基类不能仅仅用is-a 去关联, 要上升到is-like-a (并进行扩展)
向上转型虽然能实现多态, 可是缺点:扩展部分不能被基类访问。
向下转型与运行时类型识别
解决上述缺点就要使用向下转型,向下转型,需要确保哪个类型是我们希望的类型, 这样的做需要的是,这个类转型是否成功,有关于它原本的类型, 比如一个类为了实现多态向上转型, 而现在需要它的扩展功能则要向下转型, 这样就可以转型成功,可是如果是原本就是这个类, 向下转型, 调用没有的方法, 则会ClassCastEception
RTTI(Run-Time Type Identification),通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型, 也被称为downcast。