一、多态介绍
继承提高了代码的重用性,让类和类之间建立的联系,为多态创造了条件。
1、什么是多态
多态是指,在程序中定义的引用变量所指向的具体类型在编程时并不确定,而是在程序运行期间才确定。
由于在程序运行时才确定具体的类型,即不修改程序的代码就可以改变程序运行时所绑定的具体代码,让程序选择多个运行状态,这就是多态性。
2、多态的分类(主要指运行期)
编译时多态:方法重载,系统在编译时确定调用重载函数的哪个版本
运行时多态:运行时多态基于面向对象的继承性实现,它指父类型的引用可以指向子类型的对象,这就是向上转型。通过一个父类的引用发出的方法调用,执行的方法可能是在父类中的实现,也可能是在子类中的实现,这由运行时刻具体的对象类型决定。
3、向上转型
首先理解,子类是父类的特殊化,每个子类的实例也是其父类的实例,但是反过来就不成立。
所以向上转型就是在允许的继承关系中,可以将子类对象赋值给父类型的引用的特性。
反过来,会导致编译时错误。
并且,通过这个父类的引用访问子类的对象时,只能访问父类中拥有的方法和属性。因为编译器认为引用是父类中的引用。
/*Bicycle是MountainBike的父类*/ Bicycle myBike=new MountainBike(); /*Object是所有类的父类*/ Object obj=new MountainBike();
4、向下转型
即把上转型中的父类型引用又赋值给一个子类型的引用,指向的仍然是这个子类型的对象。
必须是显示的,即强制类型转换。
Object obj=new MountainBike(); MountainBike myBike=(MountainBike) obj;
这一转换编译器会通过,但是如果在运行时obj不是MountainBike类型,会抛出异常。
所以在这种情况下,为了避免运行时异常,我们可以使用instanceof操作符做逻辑测试:
if(obj instanceof MountainBike){ MountainBike myBike=(MountainBike obj); }
在一个深层次的继承关系中,这种判断分支是一个噩梦。因而在设计良好的程序中,应该尽量避免这种判断分支!
5、方法重写(override)
(父类子类同名方法的返回类型、参数的个数、类型与顺序必须保持一致)
返回类型可以不同,但必须兼容,比如父类中的类型为某个类的对象,子类中的方法可以返回这个类或者它的子类。
父类的静态方法不能被子类重写为非静态;
父类的非静态方法不能被子类重写为静态。
二、多态的实现
子类继承父类后,父类型的引用变量既可以指向父类对象,也可以指向子类对象。
当相同的消息发给一个对象引用时,该引用会根据具体指向子类还是父类对象而执行不同的行为。
多态性就是相同的消息使得不同的对象作出不同的响应的机制。
Java中有两种形式实现多态:继承和接口(接口后面讲)
1、继承实现多态(多态发生在有继承关系的父类和子类间)
通过继承实现多态有三个条件:继承关系、方法重写和向上转型(子类对象的引用赋值给父类型的引用变量)。
只有满足了3个条件,才能在一个继承结构中使用统一的逻辑代码处理不同的对象,从而达到执行不同的动作的目的。
(具体执行父类对象还是子类对象的方法,你看指向的内存是哪个的实例就好了)
public class Polymorphism { public static void main(String[] args) { Car sc = new ElectronicCar(); sc.brake(); } } class Car { public void brake() { System.out.println("SuperClass: I can brake."); } } class ElectronicCar extends Car { public void brake() { super.brake(); System.out.println("SubClass: I can prevent collision~"); } }
我们再看一个例子,更好地体会多态的优点:无论Car的子类如何变化,调用接口brakeInterface()保持不变。当brakeInterface()是给第三方使用时,接口方法保持稳定,可以不影响第三方的代码。
package test; public class Polymorphism { public static void main(String[] args) { Car sc = new Car(); brakeInterface(sc); sc=new ElectronicCar(); brakeInterface(sc); sc=new HoveringCar(); brakeInterface(sc); } public static void brakeInterface(Car sc){ sc.brake(); } } class Car { public void brake() { System.out.println("SuperClass: I can brake."); } } class HoveringCar extends Car{ public void brake(){ super.brake(); System.out.println("This is Hovering car~ "); } } class ElectronicCar extends Car { public void brake() { super.brake(); System.out.println("SubClass: I can prevent collision~"); } }
2、关于equals()方法
操作符“==”可以比较两个基本类型的变量是否相等。
当我们使用它比较两个对象的引用变量,实际上实在比较两个引用变量是否指向了相同的对象。这里,相同的对象是指在堆中占用同一块内存单元中的同类型对象。
如何比较两个对象的引用变量所指向的对象的内容是否相同?
使用equals()方法(Object类中定义了,意味着每个类都会有这个方法),返回boolean。
但是默认情况下,它比较的是对象的引用是否相同。
String、Integer、Date中,equals()方法被重写了,比较的不是内存中的地址而是对象的内容。
在自定义的类的对象中,equals()比较两个引用是否指向了同一个对象。因此,如果想比较对象的内容,在类中重写equals()吧!