1.1 多態的形式
多態是繼封裝、繼承之後,面向對象的第三大特性。
多態是出現在繼承或者實現關係中的。
多態體現的格式:
父類類型 變量名 = new 子類/實現類構造器;變量名.方法名();
多態的前提:有繼承關係,子類對象是可以賦值給父類類型的變量。例如Animal是一個動物類型,而Cat是一個貓類型。Cat繼承了Animal,Cat對象也是Animal類型,自然可以賦值給父類類型的變量。
1.2 多態的案例演示
當使用多態方式調用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤;如果有,執行的是子類重寫後方法。如果子類沒有重寫該方法,就會調用父類的該方法。
總結起來就是:編譯看左邊,運行看右邊。
代碼如下:
定義父類:
public class Animal { public void eat(){ System.out.println("動物吃東西!") }}
定義子類:
class Cat extends Animal { public void eat() { System.out.println("吃魚"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨頭"); } }
定義測試類:
public class Test {public static void main(String[] args) { // 多態形式,創建對象 Animal a1 = new Cat(); // 調用的是 Cat 的 eat a1.eat(); // 多態形式,創建對象 Animal a2 = new Dog(); // 調用的是 Dog 的 eat a2.eat(); } }
1.3 多態的定義和前提
多態: 是指同一行為,具有多個不同表現形式。
從上面案例可以看出,Cat和Dog都是動物,都是吃這一行為,但是出現的效果(表現形式)是不一樣的。
前提【重點】
繼承或者實現【二選一】方法的重寫【意義體現:不重寫,無意義】父類引用指向子類對象【格式體現】父類類型:指子類對象繼承的父類類型,或者實現的父接口類型。1.4 多態的好處
實際開發的過程中,父類類型作為方法形式參數,傳遞子類對象給方法,進行方法的調用,更能體現出多態的擴展性與便利。代碼如下:
定義父類:
public abstract class Animal { public abstract void eat(); }
定義子類:
class Cat extends Animal { public void eat() { System.out.println("吃魚"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨頭"); } }
定義測試類:
public class Test {public static void main(String[] args) { // 多態形式,創建對象 Cat c = new Cat(); Dog d = new Dog(); // 調用showCatEat showCatEat(c); // 調用showDogEat showDogEat(d); /* 以上兩個方法, 均可以被showAnimalEat(Animal a)方法所替代 而執行效果一致 */ showAnimalEat(c); showAnimalEat(d); } public static void showCatEat (Cat c){ c.eat(); } public static void showDogEat (Dog d){ d.eat(); } public static void showAnimalEat (Animal a){ a.eat(); }}
由於多態特性的支持,showAnimalEat方法的Animal類型,是Cat和Dog的父類類型,父類類型接收子類對象,當然可以把Cat對象和Dog對象,傳遞給方法。
當eat方法執行時,多態規定,執行的是子類重寫的方法,那麼效果自然與showCatEat、showDogEat方法一致,所以showAnimalEat完全可以替代以上兩方法。
不僅僅是替代,在擴展性方面,無論之後再多的子類出現,我們都不需要編寫showXxxEat方法了,直接使用showAnimalEat都可以完成。從而實現了實現類的自動切換。
所以,多態的好處,體現在,可以使程序編寫的更簡單,並有良好的擴展。
1.5 多態的弊端
我們已經知道多態編譯階段是看左邊父類類型的,如果子類有些獨有的功能,此時多態的寫法就無法訪問子類獨有功能了。
class Animal{public void eat(){ System.out.println("動物吃東西!") }}class Cat extends Animal { public void eat() { System.out.println("吃魚"); } public void catchMouse() { System.out.println("抓老鼠"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨頭"); } }class Test{ public static void main(String[] args){ Animal a = new Cat(); a.eat(); a.catchMouse();//編譯報錯,編譯看左邊,Animal沒有這個方法 }}
1.6 引用類型轉換
1.6.1 為什麼要轉型
多態的寫法就無法訪問子類獨有功能了。
當使用多態方式調用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤。也就是說,不能調用子類擁有,而父類沒有的方法。編譯都錯誤,更別說運行了。這也是多態給我們帶來的一點"小麻煩"。所以,想要調用子類特有的方法,必須做向下轉型。
回顧基本數據類型轉換
自動轉換: 範圍小的賦值給範圍大的.自動完成:double d = 5;強制轉換: 範圍大的賦值給範圍小的,強制轉換:int i = (int)3.14多態的轉型分為向上轉型(自動轉換)與向下轉型(強制轉換)兩種。
1.6.2 向上轉型(自動轉換)
向上轉型:多態本身是子類類型向父類類型向上轉換(自動轉換)的過程,這個過程是默認的。 當父類引用指向一個子類對象時,便是向上轉型。 使用格式:父類類型 變量名 =new 子類類型();如:Animal a =newCat();
原因是:父類類型相對與子類來說是大範圍的類型,Animal是動物類,是父類類型。Cat是貓類,是子類類型。Animal類型的範圍當然很大,包含一切動物。所以子類範圍小可以直接自動轉型給父類類型的變量。
1.6.3 向下轉型(強制轉換)
向下轉型:父類類型向子類類型向下轉換的過程,這個過程是強制的。 一個已經向上轉型的子類對象,將父類引用轉為子類引用,可以使用強制類型轉換的格式,便是向下轉型。使用格式:
子類類型 變量名 =(子類類型) 父類變量名;如:Aniaml a =newCat(); Cat c =(Cat) a;
1.6.4 案例演示
當使用多態方式調用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤。也就是說,不能調用子類擁有,而父類沒有的方法。編譯都錯誤,更別說運行了。這也是多態給我們帶來的一點"小麻煩"。所以,想要調用子類特有的方法,必須做向下轉型。
轉型演示,代碼如下:
定義類:
abstract class Animal { abstract void eat(); } class Cat extends Animal { public void eat() { System.out.println("吃魚"); } public void catchMouse() { System.out.println("抓老鼠"); } } class Dog extends Animal { public void eat() { System.out.println("吃骨頭"); } public void watchHouse() { System.out.println("看家"); } }
定義測試類:
public class Test {public static void main(String[] args) { // 向上轉型 Animal a = new Cat(); a.eat(); // 調用的是 Cat 的 eat // 向下轉型 Cat c = (Cat)a; c.catchMouse(); // 調用的是 Cat 的 catchMouse } }
1.6.5 轉型的異常
轉型的過程中,一不小心就會遇到這樣的問題,請看如下代碼:
public class Test {public static void main(String[] args) { // 向上轉型 Animal a = new Cat(); a.eat(); // 調用的是 Cat 的 eat // 向下轉型 Dog d = (Dog)a; d.watchHouse(); // 調用的是 Dog 的 watchHouse 【運行報錯】 } }
這段代碼可以通過編譯,但是運行時,卻報出了 ClassCastException ,類型轉換異常!這是因為,明明創建了Cat類型對象,運行時,當然不能轉換成Dog對象的。
1.6.6 instanceof關鍵字
為了避免ClassCastException的發生,Java提供了 instanceof 關鍵字,給引用變量做類型的校驗,格式如下:
變量名 instanceof 數據類型 如果變量屬於該數據類型或者其子類類型,返回true。如果變量不屬於該數據類型或者其子類類型,返回false。
所以,轉換前,我們最好先做一個判斷,代碼如下:
public class Test {
public static void main(String[] args) {
// 向上轉型
Animal a = new Cat();
a.eat(); // 調用的是 Cat 的 eat
// 向下轉型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 調用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 調用的是 Dog 的 watchHouse
}
}
}