多态原理
什么是多态
编译时多态->方法重载
运行时识别对象和类的类型信息方法:
- 反射
- RTTI(Run-Time Type Identification,运行时类型判定)
RTTI: 在编译时获取对象的类型信息.
多态:
- 一个接口的多种不同的实现方式.
- 只有在运行时才知道引用变量所指向的具体实例对象.
- 实现的三个条件:
- 继承
- 重写/实现
- 父类引用指向子类对象
多态的用法
两种实现方式
- 子类继承父类(
extends
) - 类实现接口(
implements
)
核心:声明是父类类型或接口类型,创建的是实际类型.
示例:
List list = new ArrayList(); // 声明是父类List,创建的是子类ArrayList.
public void test(List list); // 方法参数使用父类或接口.
优点:
- 灵活性:当某个实现不满足有求,则只需改动不满足要求的部分,其他部分不需要修改.
多态的实现原理与RTTI
功能:
使用父类的指针或引用来检查指针或引用所指向的对象的实际派生类型.
实现:
通过Class类文件的方法表实现.
Class类:"类的类".
- 类是对象的抽象.
- Class类是类的抽象.
- 用来创建一个类的所有对象的.
- 每个类编译后,会生成一个
.class
文件. - JVM中类加载器使用Class类生成对应的对象.
示例:
class father{}; // 父类
class son extends father{}; // 子类
father p = new son(); // 父类引用指向子类
Class c = p.getClass(); // c为子类son类型
通过JVM了解多态实现原理
- JVM执行字节码时,类型信息存放在方法区中.
- 方法区的类型信息中有一个指针,指向一张记录该类方法入口的表(方法表).
- 方法表的每一项都是指向对应方法的指针.
- 方法表的构造:
- 最先存放的是
Object
类的方法,接下来存放当前类的父类的方法,最后存放该类的方法. - 若子类改写父类的方法,子类和父类中同名方法共享一个方法表项(都被认为父类的方法).
- 只存在非私有的实例方法中,不会出现静态方法.
- 静态方法与对象无关,使用方法地址直接引用.
- 静态方法:使用
invokestatic
调用. - 私有方法或构造函数:使用
invokespecial
调用. - 方法表:只有被
invokevirtual
和invokeinterface
调用的当法存在.
- 最先存放的是
- 任何一个类的某个方法在方法表中都是固定值.
- 子类的方法表中,改写的方法的指针指向的就是子类的改写方法入口.(在编译生成子类的Class文件中就实现多态)
多态的机制:
- 调用方法.(需要完成方法的符号引用解析) ==> 符号引用被解析为方法表的偏移量.
- JVM通过对象引用 ==> 方法区中的类型信息入口 ==> 查询类的方法表 ==> 指向子类改写的方法
小结:
- 若子类改写了父类的方法,子类和父类的同名方法共享一个方法表项,都被认为时父类的方法,可以写成父类引用指向子类对象的形式.
- 子类和接口的多态实现不同,类的方法表使用固定偏移,接口需要逐个查找(接口的实现非唯一).