多态分离做什么和怎么做。又成为动态绑定,后期绑定或运行时绑定。
8.1再论向上转型
对象可以当做它的父类型来使用,称为向上转型。
一个方法接受父类为参数,所有的子类都能调用这个方法,编译器在编译时间不确定执行哪个方法,在运行时间根据相应的子类是否重写了方法决定调用哪个方法,这样做大大减少了编程。
8.2转机?
8.2.1方法调用绑定
将方法调用和方法主体关联起来被称作绑定。
后期绑定:在运行时根据对象的类型进行绑定
编译器不知道对象的类型(因为编译器只知道这是个父类,不知道是哪个子类传入的)
Java中除了static方法和final方法外都是后期绑定。
8.2.2产生正确的行为
编译器只知道对象是一种父类,只有到运行时间才知道对象具体是什么类,并执行正确的行为。
8.2.3 可扩展性
操作基类的方法不用经过任何修改!
Parent s=new Son();
发生了什么:new一个子类,为子类申请空间,但向上转型后子类增加的部分就不可见了(仍然存在!),调用方法也优先考虑子类覆盖的方法!
8.2.4 缺陷:“覆盖”私有方法
覆盖private方法不会报错,但语义不是覆盖,而是新方法,向上转型后就不可见了,达不到多态的效果。
因此,要避免“覆盖”private方法,应该起一个不同的名字!
8.2.5 缺陷:域与静态方法
域(成员变量)是在编译时间绑定的,是不会覆盖的。
如果子类和父类域名相同,则各自有各自的空间,向上转型后子类的域就不可见了(除非调用子类覆盖的方法)
实践中应该避免这种情况,首选尽量将所有的域定义为private,其次不要为子类加一个同名的域!
静态方法也不具有多态性。
8.3构造器和多态
构造器不具有多态性,(构造器是隐式static方法)
8.3.1构造器的调用顺序
父类的构造器在子类的构造器中调用,并按照继承层次逐渐向上链接。
使得对象被正确的构造。如果没有显示调用父类构造器,就会自动调用默认构造器。
调用顺序(与普通构造顺序稍微不同,父类构造器最先调用!):
按顺序调用父类构造器
按声明顺序初始化成员
调用子类构造器主体
要求在执行子类的所有操作之前,父类的一切都是已知的,所以要最先调用父类构造器!
8.3.2继承与清理
一般情况下不必执行清理动作,一旦需要手动清理,则必须谨慎加小心!!!
清理动作子类覆盖父类,并调用父类的清理动作。清理顺序与初始化顺序相反,以防对象的依赖性,避免出现问题!
如果成员对象存在这被多个对象共享的情况,问题就更加复杂了,不能简单的清理。必须使用引用计数来耿宗共享对象的对象数
8.3.3构造器内部的多态方法的行为
在构造器中调用方法,可能会出现父类构造器调用了子类覆盖方法的情况。
所以,编写构造器应该遵守原则:尽可能简单的使对象构造,避免调用其他方法,可以调用private,final等不能继承的方法。
8.4 协变返回类型
实质上是对象的向上转型。
返回类型是子类的方法的结果可以赋值给父类对象的引用。
8.5 用继承进行设计