- 面向对象三大特性之一的多态
- 多态是什么
- Java中的多态
- 运行时类别识别
运行时类型识别(RTTI, Run-Time Type Identification)
- 维护类的相关信息
- 多态是基于RTTI实现的
- RTTI主要功能由Class类实现
Class类
- Class类是”类的类”(class of classes)
如果说类是对象的抽象和集合的话,那么Class类就是对类的抽象和集合
多态的原理
- 每一个Class类的对象代表一个其他的类
- 当调用对象的getClass() 方法时,就得到对应Class对象的引用
- Java中每个对象都有相应的Class类对象,因此随时能通过Class对象知道某个对象”真正”所属的类
- 无论对引用进行怎样的类型转换,对象本身所对应的Class对象都是同一个
- 通过某个引用调用方法时,Java总能找到正确的Class类中所定义的方法,并执行该Class类中的代码
- 由于Class对象的存在,Java不会因为类型的向上转换而迷失
Class类的加载
- 当Java创建某个类的对象,比如Human类对象时,Java会检查内存中是否有相应的Class对象
- 如果内存中没有相应的Class对象,那么Java会在.class文件中寻找Human类的定义,并加载Human类的Class对象
- 在Class对象加载成功后,其他Human对象的创建和相关操作都将参照该Class对象
多态
概念
多态:一个对象变量可以指示多种实际类型的现象。
1 | graph TD |
三个必要条件
- 要有继承
- 要有重写
- 父类引用指向子类对象
Java语言中对象变量是多态的。如一个Employee变量既可以引用一个Employee类对象,也可以引用Employee类的任何一个子类的对象(Manager、Secretary、Programmer和Executive[单继承,通过Manager传递,Manager能引用Executive])。
不能将超类的引用赋给子类变量,不是所有的超类(human)都是子类(scientist)。
1 | graph TD |
动态绑定
在运行时能够自动地选择调用那个方法的现象称为动态绑定(dynamic binding)。
在执行期间(而非编译期间)判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。
方法储存在代码区(Code Seg),找到对应方法需要函数指针,而这个指针在new对象的时候被初始化,故可以迟邦定;而且方法的调用可以透过对象变量引用,看到变量的本质类型。
当程序运行,并且采用动态绑定调用方法时,虚拟机一定调用与x所引用对象的实际类型最合适的那个类的方法。假设x的实际类型是D(C的子类),如果D 类定义了该签名(方法的名字和参数列表)就直接调用,否则将在D类的超类中寻找,以此类推(超类的超类)。
动态绑定重要特性:易扩展性。
如,假设增加一个新类Executive,并且变量e有可能引用这个类的对象,我们不需要对包含调用e.f()的代码进行重新编译。如果e恰好引用一个Executive类的对象,就会自动地调用Executive.f()方法。
静态绑定
如果是方法、方法、方法或者构造器,那么编译器可以准确知道应该调用哪个方法,将这种调用方式成为静态绑定(static binding)。
对象转型(casting)
- 一个基类的引用类型变量可以“指向”其子类的对象。
- 一个基类的引用不可以访问其子类对象新增加的成员(属性和方法)。
- 可以使用用引用变量 instanceof类名来判断该引用型变量所“指向”的对象是否属于该类或该类的子类。
- 子类的对象可以当作基类的对象来使用称作向上转型(upcasting),反之称为向下转型(downcasting)。
1 | package com.yza.myJavaBasic; |
几种说法
- Java语言中对象变量是多态的。如一个Employee变量既可以引用一个Employee类对象,也可以引用Employee类的任何一个子类的对象(Manager、Secretary、Programmer和Executive[单继承,通过Manager传递,Manager能引用Executive])
不能将超类的引用赋给子类变量,不是所有的超类(human)都是子类(scientist)。 - 将一个子类的引用赋给一个超类变量,编译器是允许的。但将一个超类的引用赋给一个子类变量,必须进行类型转换(1.只能在继承层次内进行类型转换;2.在将超类转换成子类之前,应该使用instanceOf进行检查)
1
2
3
4if(a instanceof ClassB) {
objectB = (ClassB)a;
...
}