怎么可以错过(java多态实现的三种形式)java中实现多态,Java的多态如何实现的?,java实现动态多态性,
面向对象编程语言三大特征:继承、封装、多态。虽说是三大特征,但其实从多态的角度看,继承和封装都是为了实现多态而准备的,尤其是在一些大型优秀的框架上,多态的使用随处可见,所以说多态是一个十分重要的知识点。
什么是多态?
多态的定义:指允许不同类的对象对同一消息做出响应。即方法传入同一参数可以根据对象的不同而采用多种不同的处理方式。
多态的实现:动态绑定(dynamic binding),是指在执行期间判断所引用对象的实际类型,根据其实际的类型调用其相应的方法,而在java中,多态是具体表现为继承(extends)或实现(implements)。
多态的作用:可以极大消除类型之间的耦合关系,多用于架构及复用的设计中。
无论是继承还是实现,多态的核心原理都是声明的总是父类类型或接口类型,创建的是实际类型。
假如有一种场景,人开汽车,但是汽车有很多品牌有很多种类很多参数配置,这个时候该如何设计代码实现满足此种场景的要求呢?来看一下使用多态是如何完美解决的;
首先,我们考虑人开车,但不知道具体是什么车,所以我们可以将车的一些基本特征抽象成一个接口。
再定义人类,里面有开车的方法run,需要特别注意的是,这里的形参使用的是顶级接口Car,而不是具体的子类。
考虑到车还有很多品牌,每个品牌的定位及配置等都不同,这里可以再基于汽车品牌抽象出品牌车的抽象类,如国产汽车BYD是品牌,其DM系列的汉EV是具体的车型号,也就是具体的子类。
假设比亚迪DM汉EV具体的实现如下,汉是BYD品牌,所以需要继承自BYD品牌的车类。
同样的我们再写一个奔驰的品牌车类及对应的车型奔驰GLC:
奔驰GLC是具体的车型号,所以是具体的实现类
我们简单创建两个人来开车试试看,其中创建两个人分别是小明和小红,分别创建了两个车bydEV和glc,两个子类对象都指向Car顶级接口,然后运行这段代码:
最后得到的运行结果是:小明开的车上BYD汉EV,小红开的车是奔驰GLC。
在代码中明明都指向共同的一个父类接口Car,为何在运行时就不同了呢?其实这就是多态,这个底层运行实现在前文中已提到,那就是动态绑定。
动态绑定和静态绑定
JVM在执行方法时,通常调用的指令有五个,分别是:
invokestatic:调用静态方法;
invokespecial:调用实例构造器<init>方法、私有方法和父类方法;
invokevirtual:调用虚方法;
invokeinterface:调用接口方法,运行时确定具体实现;
invokedynamic:运行时动态解析所引用的方法,然后再执行,用于支持动态类型语言。
其中Invokestatic和invokespecial指令是用于静态绑定,invokevirtual 和 invokeinterface 用于动态绑定。
静态绑定指的是在编译期就能确定的,比如静态方法、构造器、私有方法和父类方法这4种方法在编译期就能解析引用确定的称为非虚方法,与之相对的就是虚方法,对象方法基本都为虚方法,例如上方例子中人类的drive(Car car),汽车子类实现类中的run()方法等。
有个特例的是被final修饰的方法,由于不能被继承重写,所以是可以唯一确定的,是属于非虚方法,但却是使用invokevirtual指令调用的。
JVM底层多态实现过程
多态的实现过程,本质就是方法调用动态绑定的过程,通过栈帧的信息去找到被调用方法的具体实现,然后使用这个具体实现的直接引用完成方法调用。
根据《Java虚拟机规范》,invokevirtual指令在运行时解析大致分为以下几个步骤:
先从操作栈中找到对象的实际类型 C;找到 C中与被调用方法签名相同的方法,如果有访问权限就返回这个方法的直接引用,如果没有访问权限就报错 java.lang.IllegalAccessError ;如果第 2 步找不到相符的方法,就去搜索 C的父类,按照继承关系自下而上依次执行第 2 步的操作;如果第 3 步找不到相符的方法,就报错 java.lang.AbstractMethodError ;可以看到,如果子类覆盖了父类的方法,则在多态调用中,动态绑定过程会首先确定实际类型是子类,从而先搜索到子类中的方法。
我们都知道在程序运行时,动态绑定是一个非常高频的行为,JVM为了提高性能,避免反复搜索对应类型的绑定数据,会建立一个对应的虚方法表和接口方法表,使用对应的表索引来提高查询的性能。
以虚方法表为例,如果某个父类的方法在子类中没有被进行重写,那么此时子类和父类的虚方法表指向的地址是一致的,只有当出现子类重写了父类的方法时,才会出现子类的虚方法表与父类的虚方法表指向的地址不一样。因此,如果子类没有重写父类的方法时,会随着父类的方法解析一同解析。
总结
多态是面向对象编程语言的重要特征,也是架构及复用设计的基础!例如文章开头的场景中,人开车,因为车有很多品牌及很多类型,车的品牌类型也无时无刻在变更,有可能明天多一个品牌,我们只需求去实现Car接口就可以轻松添加一个新的品牌,而且不用去改动原本的代码实现,所以在人类的drive不能够用具体的类型作为形参,需要使用Car顶级抽象的接口,使用多态的特性去动态绑定各种车的品牌实现。
本文系作者 @河马 原创发布在河马博客站点。未经许可,禁止转载。
暂无评论数据