前言:面向对象编程有三大特性:封装、继承和多态。封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据。对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法; 继承是为了重用父类代码,两个类若存在IS-A的关系就可以使用继承,同时继承也为实现多态做了铺垫。从一定角度来看,封装和继承几乎都是为多态而准备的,这是非常重要的知识点。多态(Polymorphism)按字面的意思就是“多种状态”。多态指同一个实体同时具有多种形式,它是面向对象程序设计(OOP)的一个重要特征。如果一个语言只支持类而不支持多态,只能说明它是是基于对象的,而不是面向对象的。

一、Java多态

Java中的多态性指同一个实体同时具有多种形式。具体体现在运行和编译两个方面。运行时多态是动态多态,其具体引用的对象在运行时才能确定。编译时多态是静态多态,在编译时就可以确定对象方法使用的形式。

1、多态的分类

对于面向对象而言,多态分为编译时多态和运行时多态。其中编译时多态是静态的,主要是指方法的重载,它是根据参数列表的不同来区分不同的函数,通过编译之后会变成两个不同的函数,在运行时谈不上多态;而运行时多态是动态的,它是通过动态绑定来实现的,也就是我们常说的多态性。

编译时期多态(又叫静态多态):

编译时期的多态是靠重载实现的,根据参数个数,类型和顺序决定的(必须在同一个类中)

运行时的多态(又叫动态多态):

在运行时JVM根据实际情况决定调用函数

2、运行时多态的概念

在前言中就提到了继承在为多态的实现做了准备:子类Child继承父类Father,我们可以编写一个指向子类的父类类型引用,该引用既可以处理父类Father对象,也可以处理子类Child对象,当相同的消息发送给子类或者父类对象时,该对象就会根据自己所属的引用而执行不同的行为,这就是多态。即多态性就是相同的消息使得不同的类做出不同的响应。  

简而言之:多态是指允许不同类的对象对相同的消息做出响应,即同一消息可以根据发送对象的不同而采用多种不同的行为方式。(发送消息就是函数调用)   

在Java中有两种形式可以实现多态:基于继承和基于接口。

a> 基于继承实现的多态

Java基于继承实现的动态多态有三个必要条件:继承、重写、向上转型。

  •  继承:在多态中必须存在有继承关系的子类和父类。
  •  重写:子类对父类中某些方法进行重新定义,在调用这些方法时就会调用子类的方法。
  •  向上转型:在多态中需要将子类的引用赋给父类对象,只有这样该引用才能够具备调用父类的方法或子类的方法的能力。

    只有满足了上述三个条件,我们才能够在同一个继承结构中使用统一的逻辑实现代码处理不同的对象,从而达到执行不同的行为。

 基于继承实现的多态可以总结如下:对于引用子类的父类类型,在处理该引用时,它适用于继承该父类的所有子类,子类对象的不同,对方法的实现也就不同,执行相同动作产生的行为也就不同。

b>基于接口实现动态多态

    基于继承实现多态是通过重写父类的同一方法的几个不同子类来体现的,那么就可就是通过实现接口并覆盖接口中同一方法的几不同的类体现的。在基于接口实现的多态中,指向接口的引用必须是指定这实现了该接口的一个类的实例程序,在运行时,根据对象引用的实际类型来执行对应的方法。

3、实现动态多态的技术称为:动态绑定(dynamic binding)

是指在执行期间JVM判断所引用对象的实际类型,根据其实际的类型调用其相应的方法。重载是静态多态,是在编译时绑定的。 与对象引用时引起的多态不同,后者是动态多态,是在运行时绑定的。

动态绑定,使用父类引用指向子类对象,再调用某一父类中的方法时,不同子类会表现出不同结果。 这样的作用就是扩展性极好,玩过网游的话应该知道, 游戏中有不同的角色,它们都有一个父类,它们做相同动作时表现出来的效果就会不一样,比如跑,魔法师的跑跟战士的跑就不会一样,这就是俩者都覆盖了父类中的跑方法,各自有自己的实现,表现出来的多态。 如果有一天你想再加个角色,只用再写一个类继承该父类,覆盖其中的跑方法就行了,其他代码不用怎么改,所以可维护性也很好。

4、多态的作用

消除类型之间的耦合关系;

简化代码,提高了代码的复用性;

提高可扩展性。

1>因为主类和其中调用的多个子类对象间的依赖不是直接的,而是通过接口或抽象类进行的,他们的耦合只在于接口层,而不是直接依赖于实现层。

2>把不同的子类对象都当作父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,极大减少了代码量,以适应需求的不断变化。赋值之后,父类型的引用就可以根据当前赋值给它的子对象的特性以不同的方式运作。

举个栗子:

//不使用多态
Cat cat=new Cat();
cat.eat();
Dog dog=new Dog();
dog.eat();
Pig pig=new Pig();
pig.eat();
......

//使用多态
public void go(Animal animal)
{
  animal.eat();
}
go(new Cat());
go(new Cat());
go(new Cat());
......

//如果有100种动物,使用多态的代码就提高代码重用性,减少了代码量

还有就是如果每个动物有100个方法 ,不使用多态岂不是每创建一个对象时都要调用100次,而多态给我们带来了好处,我们只需在基类中写出这100个方法,而在调用的时候只需将子类对象传递给基类对象,编译器将会根据具体类的对象调用相应对象的方法,从而简便了编程。

3>可扩展性:即父类为形式参数,接收任意子类对象进行具体调用

增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。