[Java编程思想] 第八章 多态
第八章 多态
“我曾经被问到‘求教,Babbage先生,如果你向机器中输入错误的数字,可以得到正确的答案吗?’我无法恰当地理解产生这种问题的概念上地混淆” ——Charles Babbage(1791-1871)
再面向对象地程序设计语言中,多态是继数据抽象和继承之后的第三种基本特征。
“封装”通过合并特征和行为来创建新的数据类型。“实现隐藏”则通过讲细节“私有化”把接口和实现分离开来。继承允许将对象视为它自己本身的类型或其基类类型来处理。多态方法调用允许一种类型表现出与其他相似类型之间的区别,只要它们都是从同一基类导出来的。这种区别是根据方法行为的不同表现出来的,虽然这些方法都可以通过同一个基类来调用。
继承与多态:
继承:类与类之间的关系
多态:方法行为的不同表现
8.1 再论向上转型
对象既可以作为它自身的类型使用,也可以作为它的基类使用。
public enum Note{} class Obj{ public void play(Note n){ System.out.println("Obj.play()"); } } class A extends Obj{ public void play(Note n){ System.out.println("A.play()"); } } class M{ public static void tune(Obj o){ // 提供向上转型方法 o.play(Note); } public static void main(String[] args){ A a = new A(); tune(a); // 向上转型,将A转为Obj } }
M.tune()可以接受Obj类型,也可以接受任何导出自Obj的类。
8.1.1 忘记对象类型
忘记对象的类型,只记住它们的基类。
class B extends Obj{ public void play(Note n){ System.out.println("B.play()"); } } class C extends Obj{ public void play(Note n){ System.out.println("C.play()"); } } class M{ public static void tune(A o){ // 为每一个新Obj导出类编写方法 o.play(Note); } public static void tune(B o){ o.play(Note); } public static void tune(C o){ o.play(Note); } public static void main(String[] args){ A a = new A(); B b = new B(); C c = new C(); tune(a); tune(b); tune(c); } }
可以这样做,但有一个主要缺点:必须为每一个新Obj类编写特定代码。
8.2 转机
观察tune()方法:
public static void tune(Obj o){ o.play(Note); }
接受一个Obj引用,编译器怎么知道这个Obj是A还是B?
8.2.1 方法调用绑定
将一个方法调用同一个方法主体关联起来被称作绑定。
上述程序疑问,主要是因为前期绑定。解决的办法就是后期绑定,它的含义就是在运行时根据对象的类型进行绑定。就是说,编译器一直不知道类型,但是方法调用机制能找到正确的方法体,并加以调用。
Java中除了static方法和final方法(private属于final)之外,其他所有的方法都是后期绑定。通常我们不必判断,因为它会自动发生。
8.2.2 产生正确的形为
了解Java所有方法都是通过动态绑定实现多态之后,就可以只编写与基类打交道的程序代码了。
在编译时,编译器不需要获得任何特殊信息就能进行正确调用。
8.2.3 缺陷:“覆盖”私有方法
只有非private方法才能被覆盖,但是还需要注意覆盖private方法的现象,重写的private是全新的方法。
8.2.4 缺陷:域和静态方法
多态只适用于普通方法,不适用于类成员变量。
静态方法不具有多态性,静态方法是与类,而非与单个的对象相关联。
8.3 构造器和多态
尽管构造器并不具有多态性(实际上是隐式static方法),但了解构造器的运作有助于避免一些困扰。
8.3.1 构造器的调用顺序
基类构造器总是在导出类构造过程中被调用,而且按照继承层次逐渐向上,因为构造器有一项特殊任务:检查对象是否被正确构造。导出类只能访问自己的成员,基类成员通常是private,只有基类构造器才能恰当的对自己的元素进行初始化。因此必须另所有构造器都得到调用,否则就不可能正确构建完整对象。
构造器调用顺序:
- 调用基类构造器。逐层调用。
- 按声明顺序调用(基类)成员的初始化方法。
- 调用导出类构造器主体。
8.3.2 构造器内部的多态方法的形为
如果在一个构造器的内部调用正在构造的对象的某个动态绑定(重写)方法,那会发生什么情况?
当在基类构造器中调用导出类重写过的方法,实际运行的是导出类的方法,并且这个导出类的成员可能还未初始化。
构造器中唯一安全调用的方法是基类中final(final包括private)方法,因为这些方法不能被覆盖。
class A{ void draw(){System.out.println("A.draw()");} A(){ System.out.println("A() bdfore draw()"); draw(); // 基类中多态调用,实际执行B.draw()方法 System.out.println("A() after draw()"); } } class B extends A{ private int radius = 1; B(int r){ radius = r; System.out.println("B.B(), radius = " + radius); //初始化完成,构造器赋值radius=5 } void draw(){ System.out.println("B.draw(), radius = " + radius); } } public class M{ public static void main(String[] args){ new B(5); } }
输出
A() bdfore draw() B.draw(), radius = 0 // 基类调用的实际是导出类多态方法,此时导出类成员还未初始化完成 A() after draw() B.B(), radius = 5 // 此时整个初始化完成
前一节讲述的初始化顺序并不十分完整,初始化实际过程是:
- 在其他任何事务发生前,将分配给对象的储存空间初始化成二进制的零。
- 如前所述那样调用基类构造器,此时,调用覆盖后(多态)的方法,由于步骤1的关系,多态方法中用到的成员属性radius值为0;
- 按照声明的顺序调用成员的初始化方法。
- 调用导出类的构造器主体。
8.4 用继承进行设计
用继承表达行为间的差异,并用字段表达状态上的变化。
更好的方式是首先选择“组合”,尤其是不能十分确定应该使用哪一种方式时,组合不会强制我们的程序设计进入继承的层次结构。
class O{ public void act(){} } class A extends O{ public void act(){System.out.println("A...");} } class B extends O{ public void act(){System.out.println("B...");} } class S{ // 组合 private O o = new A(); public void change(){o = new B();} public void performPlay(){o.act();} } public class M{ public static void main(String[] args){ S s = new S(); // S 成员初始化是 A s.performPlay(); // S 调用默认是 A s.change(); // S 修改状态为 B s.performPlay(); // S 成员变成 B } }
输出
A... B...
通过继承得到两个不同的类,用于表达act()方法的差异;而S通过运用组合是自己的状态发生变化,这种情况下,这种状态的改变也产生了应为的改变
8.5 向下转型和运行时类型识别
Java中所有转型都会得到检查!
8.6 总结
多态意味着“不同的形式”。在面向对象程序设计中,我们持有从基类继承而来的相同接口,以及使用该接口的不同形式:不同版本的动态绑定方法。
- Java编程思想 第八章读书笔记-多态
- Java编程思想_第八章_多态笔记
- JAVA编程思想-第八章 多态
- Java编程思想笔记第八章(多态)
- 多态-java编程思想-第八章
- 【Java编程思想笔记】第八章-多态
- 《Java 编程思想》--第八章:多态
- JAVA编程思想学习总结:第八章——多态
- java编程思想之多态
- Java编程思想:第8章 多态
- Java编程思想-多态的缺陷
- JAVA编程思想学习 --- 第八章 (对象的容纳)
- java编程思想笔记--多态部分五
- Java编程思想之多态
- Java编程思想学习之多态动态绑定
- Java编程思想_多态
- java编程思想学习-第8章多态
- java 编程思想笔记之多态
- Java 编程思想(第四版)学习笔记(8)多态
- [015]Java编程思想——多态