面向?qū)ο笕蠡咎卣髦憾鄳B(tài)
多態(tài)的概述,多態(tài)的形式
-
多態(tài)的定義:
- 同類型的對(duì)象,執(zhí)行同一個(gè)行為,會(huì)表現(xiàn)出不同的行為特征
-
多態(tài)的常見形式:
父類類型 對(duì)象名稱 = new 子類構(gòu)造器; 接口 對(duì)象名稱 = new 實(shí)現(xiàn)類構(gòu)造器;-
測(cè)試案例:
package com.java.PolymorphicTest; public abstract class Animal { // 為方便測(cè)試統(tǒng)一采用public public String name = "父類動(dòng)物"; public abstract void run(); }package com.java.PolymorphicTest; public class Dog extends Animal { public String name = "子類狗"; @Override public void run() { System.out.println("小狗跑的很快!"); } }package com.java.PolymorphicTest; public class Tortoise extends Animal { public String name = "子類烏龜"; @Override public void run() { System.out.println("烏龜跑的很慢!"); } }package com.java.PolymorphicTest; public class Test { public static void main(String[] args) { Animal d = new Dog(); d.run(); System.out.println(d.name); System.out.println("========================"); Animal t = new Tortoise(); t.run(); System.out.println(d.name); System.out.println("============多態(tài)第二個(gè)優(yōu)勢(shì),父類作為方法形參,可以將所有子類對(duì)象傳進(jìn)來============"); go(d); go(t); } /** * 多態(tài)第二個(gè)優(yōu)勢(shì) */ public static void go(Animal a) { a.run(); } } /* 小狗跑的很快! 父類動(dòng)物 ======================== 烏龜跑的很慢! 父類動(dòng)物 ============多態(tài)第二個(gè)優(yōu)勢(shì),父類作為方法形參,可以將所有子類對(duì)象傳進(jìn)來============ 小狗跑的很快! 烏龜跑的很慢! */
-
多態(tài)中成員訪問特點(diǎn):
-
成員方法調(diào)用:編譯看左邊,運(yùn)行看右邊
編譯的時(shí)候會(huì)看向左邊父類的成員方法,具體運(yùn)行時(shí)會(huì)運(yùn)行右邊子類對(duì)象的方法
-
成員變量調(diào)用:編譯看左邊,運(yùn)行也看左邊(多態(tài)是側(cè)重行為的多態(tài))
成員變量的調(diào)用,不管編譯還是運(yùn)行,都看向父類的一方(如上述測(cè)試代碼name的值均為父類動(dòng)物)
-
-
多態(tài)的前提:
- 有繼承/實(shí)現(xiàn)關(guān)系
- 有父類引用指向子類對(duì)象
- 有方法重寫
多態(tài)的好處
-
在多態(tài)形勢(shì)下,右邊對(duì)象可以實(shí)現(xiàn)解耦合,便于擴(kuò)展和維護(hù)
組件化的解耦合即組件之間的依賴沒有那么強(qiáng)
Animal a = new Dog(); a.run(); //當(dāng)Dog類換成其他類,即對(duì)象發(fā)生了變化,后續(xù)業(yè)務(wù)行為隨對(duì)象而變,后續(xù)的代碼邏輯無需修改 Animal a = new Cat(); a.run(); -
定義方法的時(shí)候,使用父類型作為參數(shù),該方法就可以接收這父類的一切子類對(duì)象,體現(xiàn)出多態(tài)的擴(kuò)展性與便利
參照上方測(cè)試案例代碼
- 多態(tài)下會(huì)產(chǎn)生的一個(gè)問題:
- 多態(tài)下不能使用 子類的獨(dú)有功能
多態(tài)下引用數(shù)據(jù)類型的類型轉(zhuǎn)換
為了解決多態(tài)下不能調(diào)用子類獨(dú)有功能的弊端提出引用數(shù)據(jù)類型的類型轉(zhuǎn)換
自動(dòng)類型轉(zhuǎn)換(從子到父):子類對(duì)象可以直接賦值給父類類型的變量指向
-
強(qiáng)制類型轉(zhuǎn)換(從父到子):
此時(shí)必須進(jìn)行強(qiáng)制類型轉(zhuǎn)換:子類 對(duì)象變量 = (子類)父類類型的變量
作用:可以解決多態(tài)下的劣勢(shì),可以實(shí)現(xiàn)調(diào)用子類獨(dú)有的功能
-
注意:如果轉(zhuǎn)型后的類型和對(duì)象真實(shí)類型不是同一種類型,那么在轉(zhuǎn)換的時(shí)候就會(huì)出現(xiàn)ClassCastException
強(qiáng)制類型轉(zhuǎn)換,編譯階段不報(bào)錯(cuò)的(注意:有繼承或者實(shí)現(xiàn)關(guān)系編譯階段可以強(qiáng)制轉(zhuǎn)換,但是運(yùn)行時(shí)可能出錯(cuò))
例如,Dog d = (Dog) a2;編譯器認(rèn)為程序員希望將Animal類的對(duì)象a2轉(zhuǎn)換成Dog類型并指向Dog類型的d,因此編譯階段放行,但是在運(yùn)行時(shí)發(fā)現(xiàn),d不是Dog的對(duì)象,而是烏龜?shù)膶?duì)象,指向有問題,張冠李戴,于是報(bào)錯(cuò)
-
Java建議強(qiáng)轉(zhuǎn)前使用instanceof關(guān)鍵字判斷當(dāng)前對(duì)象的真實(shí)類型,然后再強(qiáng)制轉(zhuǎn)換
變量名 instanceof 真實(shí)類型 // 判斷關(guān)鍵字左邊的變量指向的對(duì)象的真實(shí)類型,是否是右邊的類型或者是其子類型,是則返回true,反之返回false
-
測(cè)試代碼:
package com.java.PolymorphicDisadvantages; public class Animal { public void run(){ System.out.println("動(dòng)物可以跑!"); } }package com.java.PolymorphicDisadvantages; public class Dog extends Animal { @Override public void run() { System.out.println("??跑的快"); } /** * 獨(dú)有功能 */ public void lookDoor() { System.out.println("??看門"); } }package com.java.PolymorphicDisadvantages; public class Tortoise extends Animal { @Override public void run() { System.out.println("??跑的很慢!"); } /** * 獨(dú)有功能 */ public void layEggs() { System.out.println("??可以下蛋"); } }package com.java.PolymorphicDisadvantages; public class Test { public static void main(String[] args) { // 自動(dòng)類型轉(zhuǎn)換 Animal a = new Dog(); a.run(); // 強(qiáng)制類型轉(zhuǎn)換 Animal a2 = new Tortoise(); Tortoise t = (Tortoise) a2; Dog d = (Dog) a; d.lookDoor(); t.layEggs(); System.out.println(d instanceof Dog); // true System.out.println(t instanceof Tortoise); // true System.out.println(d instanceof Tortoise); // 在編譯階段直接報(bào)紅,由此可知類型不匹配 } }
多態(tài)的綜合案例
-
需求:
- 使用面向?qū)ο缶幊棠M:設(shè)計(jì)一個(gè)電腦對(duì)象,可以安裝2個(gè)USB設(shè)備
- 鼠標(biāo):被安裝時(shí)可以完成接入,調(diào)用點(diǎn)擊功能,拔出功能
- 鍵盤:被安裝時(shí)可以完成接入,調(diào)用打字功能,拔出功能
-
分析:
- 定義一個(gè)USB接口(申明USB設(shè)備的規(guī)范是:可以接入和拔出)
- 提供兩個(gè)USB實(shí)現(xiàn)類代表鼠標(biāo)和鍵盤,讓其實(shí)現(xiàn)USB接口,并分別定義獨(dú)有功能
- 創(chuàng)建電腦對(duì)象,創(chuàng)建2個(gè)USB實(shí)現(xiàn)類對(duì)象,分別安裝到電腦中并觸發(fā)功能的執(zhí)行
-
案例代碼:
package com.java.Polymorphic_Case; /** * usb接口規(guī)范 */ public interface USB { void connect(); void unconnect(); }package com.java.Polymorphic_Case; /** * 鼠標(biāo)實(shí)現(xiàn)類 */ public class Mouse implements USB { private String goodsName; public Mouse() { } public Mouse(String goodsName) { this.goodsName = goodsName; } public String getGoodsName() { return goodsName; } public void setGoodsName(String name) { this.goodsName = name; } @Override public void connect() { System.out.println(getGoodsName() + "鼠標(biāo)成功接入"); } @Override public void unconnect() { System.out.println(getGoodsName() + "鼠標(biāo)成功斷開"); } /** * 鼠標(biāo)獨(dú)有功能:點(diǎn)擊 */ public void onClick() { System.out.println("使用" + getGoodsName() + "鼠標(biāo)點(diǎn)擊了IDEA中的運(yùn)行按鈕"); } }package com.java.Polymorphic_Case; import java.util.Scanner; /** * 鍵盤實(shí)現(xiàn)類 */ public class KeyBoard implements USB { private String goodsName; public KeyBoard() { } public KeyBoard(String goodsName) { this.goodsName = goodsName; } public String getGoodsName() { return goodsName; } public void setGoodsName(String goodsName) { this.goodsName = goodsName; } @Override public void connect() { System.out.println(getGoodsName() + "鍵盤成功接入"); } @Override public void unconnect() { System.out.println(getGoodsName() + "鍵盤成功斷開"); } /** * 鍵盤獨(dú)有功能:打字 */ public void keyDone() { Scanner scanner = new Scanner(System.in); System.out.println("請(qǐng)敲擊鍵盤"); String str = scanner.next(); System.out.println("使用" + getGoodsName() + "鍵盤敲擊出一串內(nèi)容:" + str); } }package com.java.Polymorphic_Case; public class Computer { private String goodsName; public Computer() { } public Computer(String goodsName) { this.goodsName = goodsName; } public String getGoodsName() { return goodsName; } public void setGoodsName(String goodsName) { this.goodsName = goodsName; } /** * 提供安裝USB設(shè)備的入口 */ public void installUSB(USB usb) { //多態(tài)用法 usb.connect(); // 獨(dú)有功能調(diào)用 if (usb instanceof KeyBoard) { KeyBoard k = (KeyBoard) usb; k.keyDone(); } else if (usb instanceof Mouse) { Mouse m = (Mouse) usb; m.onClick(); } usb.unconnect(); } public void start() { System.out.println(getGoodsName() + "電腦開機(jī)了"); } }package com.java.Polymorphic_Case; public class Test { public static void main(String[] args) { Computer c = new Computer(); c.setGoodsName("Thinkpad"); c.start(); USB u = new KeyBoard("HHKB"); c.installUSB(u); USB v = new Mouse("Razer"); c.installUSB(v); } }