1、什麼是多態函數
「多態」是JAVA的一種重要特性,能夠理解爲事物存在的多種形態。優化
不過,這只是字面上來理解,等於廢話。那麼究竟何爲多種形態呢,接下來,舉一個現實生活中的例子。spa
好比,動物裏有貓和狗。貓擺在面前,你可說它是貓,也能夠說它是動物。code
說它是貓時,用JAVA語句表示即 貓 x=new 貓;對象
說它是動物時,用JAVA語句表示即 動物 x=new 貓;blog
這樣,實體x即具有貓的類型,也具有動物類型。但必須一個前提,即「貓」必須是「動物」中的一種,若是「狗 x=new 貓」就不對了。繼承
經過以上的例子,咱們能夠看出,實體除了具有本類類型,還能夠具有其它類型。這種是「多態」。編譯
先看如下代碼,這是使用非多態方式編寫的class
代碼以下:變量
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("拱地"); } }
2、多態的代碼提現形式
父類的引用引向本身的子類對象。
父類的引用也能夠接收本身的子類對象。如以上代碼中:Animal a=new Cat()
3、多態的做用
提升了程序的擴展性
4、多態的前提
類與類有關係,必須是繼承或是實現。如以上代碼中,Cat、Dog、Pig類都繼承了Animal類
一般還有一個前提,就是「複寫」。如以上代碼中,子父類中都有eat()方法,子類複寫父類。
5、多態的弊端
提升了擴展性,可是隻能是父類的引用訪問父類中的成員
6、多類中數據的轉型
在基本數據類型中,存在着數據類型提高現象,如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(); //輸出貓的特有方法
在多態中,成員函數的特色:
一、編輯時期,參閱引用型變量所屬的類中是否有調用方法。若是有編譯經過,不然編譯失敗。
以下:Fu f = new Zi(),f所屬的Fu中只有method1和method2,因此輸出method3會編輯失敗。
二、在運行時間,參閱對象所屬的類中是否有調用方法。
以下:
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_4zi_method_4