多态
一、什么是多态
“多态”是JAVA的一种重要特性,可以理解为事物存在的多种形态。
不过,这只是字面上来理解,等于废话。那么究竟何为多种形态呢,接下来,举一个现实生活中的例子。
比如,动物里有猫和狗。猫摆在面前,你可说它是猫,也可以说它是动物。
说它是猫时,用JAVA语句表示即 猫 x=new 猫;
说它是动物时,用JAVA语句表示即 动物 x=new 猫;
这样,实体x即具备猫的类型,也具备动物类型。但必须一个前提,即“猫”必须是“动物”中的一种,如果“狗 x=new 猫”就不对了。
通过以上的例子,我们可以看出,实体除了具备本类类型,还可以具备其它类型。这种是“多态”。
先看以下代码,这是使用非多态方式编写的
代码如下:
package com.duotai; public class DuoTaiDemo { public static void main(String[] args) { myFun(new Cat()); myFun(new Dog()); myFun(new Pig()); } // 动物“吃”的功能提取出来封装成函数 public static void myFun(Cat c) { c.eat(); } public static void myFun(Dog d) { d.eat(); } public static void myFun(Pig p) { p.eat(); } } // 以下定义抽象类,定义了动物有“吃”的功能 abstract class Animal { abstract void eat(); // 动物吃什么不知道,定义成抽象。定义体系中的基本功能。只要继承了这个类,必有此功能。 } // 以定义具体类,复写动物的吃的功能,并有自己独特的功能 class Cat extends Animal { public void eat() { // 复写Animal类的eat方法 System.out.println("猫吃鱼"); } public void catchMouse() { // 自己特有的方法 System.out.println("猫抓老鼠"); } } class Dog extends Animal { public void eat() { // 复写Animal类的eat方法 System.out.println("狗吃骨头"); } public void kanJia() { // 自己特有的方法 System.out.println("看家"); } } class Pig extends Animal { public void eat() { // 复写Animal类的eat方法 System.out.println("猪吃饲料"); } public void gongDi() { // 自己特有的方法 System.out.println("拱地"); } }
以上代码,定义了一个“动物”类,其有一个“吃”的功能,但是由于每种动物吃的方式都不一样,所以定义成抽象类。以后让具体的动物去复写。
然后,定义了三个继承类,“猫”“狗”“猪”,复写了动物的“吃”的功能。
主函数调用时,要建立子类对的引用对象,再调用吃的方式。
这样编写时,会有一个弊端,那就是继承的子类如果增加时,则要修改主函数中的“吃”的代码。子类越多,这端代码就越长,扩展性比较弱。
如何优化呢?
子类“猫”,继承了“动物”类,是动物中的一种,那就可以用“动物”的引用来指向子类实例,即Aniaml c=new cat,c.eat(),运行结果是子类的,因为父类中有,子类中也有,就运行子类的eat(),因为子类复写了。这就是一个事物具备多种形态。
代码简化如下:
package com.duotai; public class DuoTaiDemo { public static void main(String[] args) { myFun(new Cat()); myFun(new Dog()); myFun(new Pig()); } // 动物“吃”的功能提取出来封装成函数 public static void myFun(Animal a){ //只要定义Animal即可。相当于Animal a=new Cat(); a.eat(); } } // 以下定义抽象类,定义了动物有“吃”的功能 abstract class Animal { abstract void eat(); // 动物吃什么不知道,定义成抽象。定义体系中的基本功能。只要继承了这个类,必有此功能。 } // 以定义具体类,复写动物的吃的功能,并有自己独特的功能 class Cat extends Animal { public void eat() { // 复写Animal类的eat方法 System.out.println("猫吃鱼"); } public void catchMouse() { // 自己特有的方法 System.out.println("猫抓老鼠"); } } class Dog extends Animal { public void eat() { // 复写Animal类的eat方法 System.out.println("狗吃骨头"); } public void kanJia() { // 自己特有的方法 System.out.println("看家"); } } class Pig extends Animal { public void eat() { // 复写Animal类的eat方法 System.out.println("猪吃饲料"); } public void gongDi() { // 自己特有的方法 System.out.println("拱地"); } }
二、多态的代码提现形式
父类的引用引向自己的子类对象。
父类的引用也可以接收自己的子类对象。如以上代码中:Animal a=new Cat()
三、多态的作用
提高了程序的扩展性
四、多态的前提
类与类有关系,必须是继承或是实现。如以上代码中,Cat、Dog、Pig类都继承了Animal类
通常还有一个前提,就是“复写”。如以上代码中,子父类中都有eat()方法,子类复写父类。
五、多态的弊端
提高了扩展性,但是只能是父类的引用访问父类中的成员
六、多类中数据的转型
在基本数据类型中,存在着数据类型提升现象,如double=2.3+1,会将1由int提升为double
在多类中,引用数据也存在数据提升。如以上Animal a=new Cat()中,Animal是父类型,Cat是子类型,将Cat提升为Animal,称为向上转型。
如果想要调用调用猫的特有方法(抓老鼠)时,可以强制将父类的引用转成子类对象。
我们能转换的是父类引用指向自己的子类对象,该引用可以被提升,也可被强制向下转换
如下:
Animal c=new Cat();// Cat c1=(Cat)c; //向下转型 c1.catchMouse(); //输出猫的特有方法
在多态中,成员函数的特点:
1、编辑时期,参阅引用型变量所属的类中是否有调用方法。如果有编译通过,否则编译失败。
如下:Fu f = new Zi(),f所属的Fu中只有method1和method2,所以输出method3会编辑失败。
2、在运行时间,参阅对象所属的类中是否有调用方法。
如下:
Fu f = new Zi();
f.method1();
f.method2();
调用的是Zi类的方法,
简单总结,成员函数在多态调用时,编辑看左边,运行看右边
如下:
Fu类有method1和method2两个方法
Zi类中有method1和method3两个方法
Zi类中method1复写了Fu类中的method1,Zi类有三个方法
package com.duotai2; public class DuoTaiDemo { public static void main(String[] args) { Fu f = new Zi(); f.method1(); f.method2(); // f.method3(); //编译失败 } } class Fu { void method1() { // System.out.println("fu_method_1"); } void method2() { // System.out.println("fu_method_2"); } } class Zi extends Fu { void method1() { //复写 System.out.println("zi_method_1"); } void method3() { // System.out.println("zi_method_3"); } }
输出:
zi_method_1
fu_method_2
在多态中,成员变量的特点:
无论编辑和运行,只参考左边。如下
package com.duotai2; public class DuoTaiDemo { public static void main(String[] args) { Fu f = new Zi(); System.out.println(f.num); Zi z=new Zi(); System.out.println(z.num); } } class Fu { int num=5; } class Zi extends Fu { int num=8; }
输出:
5
8
在多态中,静态成员函数的特点:
无论编辑和运行,只参考左边。因为静态方法不需要创建对象,只要类名调用即可。
如下
public class DuoTaiDemo { public static void main(String[] args) { Fu f = new Zi(); f.method4(); Zi z=new Zi(); z.method4(); } } class Fu { static void method4() { // System.out.println("fu_method_4"); } } class Zi extends Fu { static void method4() { // System.out.println("zi_method_4"); } }
输出:
fu_method_4
zi_method_4