Java 多態
本小節我們來學習面向對象的最后一大特征——多態。多態是面向對象最重要的特性。我們將介紹多態的概念和特點,并帶領大家實現一個多態的案例,你將了解到多態的實現條件、什么是向上轉型以及什么是向下轉型,并學會使用instanceof
運算符來檢查對象引用是否是類型的實例。
1. 概念和特點
多態顧名思義就是多種形態,是指對象能夠有多種形態。在面向對象中最常用的多態性發生在當父類引用指向子類對象時。在面向對象編程中,所謂多態意指相同的消息給予不同的對象會引發不同的動作。換句話說:多態意味著允許不同類的對象對同一消息做出不同的響應。
例如,火車類和飛機類都繼承自交通工具類,這些類下都有各自的run()
方法,交通工具的run()
方法輸出交通工具可以運輸,而火車的run()
方法輸出火車會跑,飛機的run()
方法則輸出飛機會飛,火車和飛機都繼承父類的run()
方法,但是對于不同的對象,擁有不同的操作。
任何可以通過多個IS-A
測試的 Java 對象都被視為多態的。在 Java 中,所有 Java 對象都是多態的,因為任何對象都能夠通過IS-A
測試以獲取其自身類型和 Object 類。
2. 實現多態
2.1 實現條件
在 Java 中實現多態有 3 個必要條件:
- 滿足繼承關系
- 要有重寫
- 父類引用指向子類對象
2.1 實例
例如,有三個類Pet
、Dog
、Cat
:
父類Pet:
class Pet {
// 定義方法 eat
public void eat() {
System.out.println("寵物吃東西");
}
}
子類Dog繼承Pet
class Dog extends Pet { // 繼承父類
// 重寫父類方法 eat
public void eat() {
System.out.println("狗狗吃狗糧");
}
}
子類Cat繼承Pet
class Cat extends Pet { // 繼承父類
// 重寫父類方法 eat
public void eat() {
System.out.println("貓貓吃貓糧");
}
}
在代碼中,我們看到Dog
和Cat
類繼承自Pet
類,并且都重寫了其eat
方法。
現在已經滿足了實現多態的前兩個條件,那么如何讓父類引用指向子類對象呢?我們在main
方法中編寫代碼:
public void main(String[] args) {
// 分別實例化三個對象,并且保持其類型為父類Pet
Pet pet = new Pet();
Pet dog = new Dog();
Pet cat = new Cat();
// 調用對象下方法
pet.eat();
dog.eat();
cat.eat();
}
運行結果:
寵物吃東西
狗狗吃狗糧
貓貓吃貓糧
在代碼中,Pet dog = new Dog();
、Pet cat = new Cat();
這兩個語句,把Dog
和Cat
對象轉換為Pet
對象,這種把一個子類對象轉型為父類對象的做法稱為向上轉型。父類引用指向了子類的實例。也就實現了多態。
2.3 向上轉型
向上轉型又稱為自動轉型、隱式轉型。向上轉型就是父類引用指向子類實例,也就是子類的對象可以賦值給父類對象。例如:
Pet dog = new Dog();
這個是因為Dog
類繼承自Pet
類,它擁有父類Pet
的全部功能,所以如果Pet
類型的變量指向了其子類Dog
的實例,是不會出現問題的。
向上轉型實際上是把一個子類型安全地變成了更加抽象的父類型,由于所有類的根類都是Object
,我們也把子類類型轉換為Object
類型:
Cat cat = new Cat();
Object o = cat;
2.4 向下轉型
向上轉型是父類引用指向子類實例,那么如何讓子類引用指向父類實例呢?使用向下轉型就可以實現。向下轉型也被稱為強制類型轉換。例如:
// 為Cat類增加run方法
class Cat extends Pet { // 繼承父類
// 重寫父類方法 eat
public void eat() {
System.out.println("貓貓吃貓糧");
}
public void run() {
System.out.println("貓貓跑步");
}
public static void main(String[] args) {
// 實例化子類
Pet cat = new Cat();
// 強制類型轉換,只有轉換為Cat對象后,才能調用其下面的run方法
Cat catObj = (Cat)cat;
catObj.run();
}
}
運行結果:
貓貓跑步
我們為Cat
類新增了一個run
方法,此時我們無法通過Pet
類型的cat
實例調用到其下面特有的run
方法,需要向下轉型,通過(Cat)cat
將Pet
類型的對象強制轉換為Cat
類型,這個時候就可以調用run
方法了。
使用向下轉型的時候,要注意:不能將父類對象轉換為子類類型,也不能將兄弟類對象相互轉換。以下兩種都是錯誤的做法:
// 實例化父類
Pet pet = new Pet();
// 將父類轉換為子類
Cat cat = (Cat) pet;
// 實例化Dog類
Dog dog = new Dog();
// 兄弟類轉換
Cat catObj = (Cat) dog;
不能將父類轉換為子類,因為子類功能比父類多,多的功能無法憑空變出來。兄弟類之間不能轉換,這就更容易理解了,兄弟類之間同樣功能不盡相同,不同的功能也無法憑空變出來。
3. instanceof 運算符
instanceof
運算符用來檢查對象引用是否是類型的實例,或者這個類型的子類,并返回布爾值。如果是返回true
,如果不是返回false
。通常可以在運行時使用 instanceof
運算符指出某個對象是否滿足一個特定類型的實例特征。其使用語法為:
<對象引用> instanceof 特定類型
例如,在向下轉型之前,可以使用instanceof
運算符判斷,這樣可以提高向下轉型的安全性:
Pet pet = new Cat();
if (pet instanceof Cat) {
// 將父類轉換為子類
Cat cat = (Cat) pet;
}
4. 小結
通過本小節的學習,我們知道了多態意味著一個對象有著多重特征,可以在特定的情況下,表現出不同狀態,從而對應著不同的屬性和方法。實現多態有 3 個必要條件,分別是要有繼承、要有重寫以及父類引用指向子類對象,通過向上轉型可以使父類引用指向子類實例;通過向下轉型可以使子類引用指向父類實例,使用instanceof
運算符可以用來檢查對象引用是否是類型的實例。