多態(tài)概述
多態(tài)的格式和使用
代碼當中體現(xiàn)多態(tài)性,其實就是一句話:父類引用指向子類對象。
格式:
- 父類名稱 對象名 = new 子類名稱();
- 接口名稱 對象名 = new 實現(xiàn)類名稱();
FU類:
public class Fu {
public void method() {
System.out.println("父類方法");
}
public void methodFu() {
System.out.println("父類特有方法");
}
}
ZI類:
public class Zi extends Fu {
@Override
public void method() {
System.out.println("子類方法");
}
}
main方法:
public class Demo01Multi {
public static void main(String[] args) {
// 使用多態(tài)的寫法
// 左側(cè)父類的引用浅妆,指向了右側(cè)子類的對象
Fu obj = new Zi();
obj.method();
obj.methodFu();
}
}
運行:
多態(tài)中成員變量和成員方法的使用特點
訪問成員變量的兩種方式:
- 直接通過對象名稱訪問成員變量:看等號左邊是誰皿渗,優(yōu)先用誰樊拓,沒有則向上找洁闰。
- 間接通過成員方法訪問成員變量:看該方法屬于誰,優(yōu)先用誰赘阀,沒有則向上找益缠。
在多態(tài)的代碼當中,成員方法的訪問規(guī)則是:
看new的是誰基公,就優(yōu)先用誰幅慌,沒有則向上找。
口訣:編譯看左邊轰豆,運行看右邊胰伍。
對比一下:
成員變量:編譯看左邊,運行還看左邊秒咨。
成員方法:編譯看左邊喇辽,運行看右邊。
意思是:在調(diào)用成員變量或者成員方法時雨席,要看等號左邊的類中是否有該變量或者該方法菩咨。
FU類:
public class Fu /*extends Object*/ {
int num = 10;
public void showNum() {
System.out.println(num);
}
public void method() {
System.out.println("父類方法");
}
public void methodFu() {
System.out.println("父類特有方法");
}
}
ZI類:
public class Zi extends Fu {
int num = 20;
int age = 16;
@Override
public void showNum() {
System.out.println(num);
}
@Override
public void method() {
System.out.println("子類方法");
}
public void methodZi() {
System.out.println("子類特有方法");
}
}
main方法:
public class Demo01MultiField {
public static void main(String[] args) {
// 使用多態(tài)的寫法,父類引用指向子類對象
Fu obj = new Zi();
System.out.println(obj.num); // 父:10
// System.out.println(obj.age); // 錯誤寫法陡厘!
System.out.println("=============");
// 子類沒有覆蓋重寫抽米,就是父:10
// 子類如果覆蓋重寫,就是子:20
obj.showNum();
//成員方法的使用
obj.method(); // 父子都有糙置,優(yōu)先用子
obj.methodFu(); // 子類沒有云茸,父類有,向上找到父類
// 編譯看左邊谤饭,左邊是Fu标捺,F(xiàn)u當中沒有methodZi方法懊纳,所以編譯報錯。
// obj.methodZi(); // 錯誤寫法亡容!
}
}
總結(jié):
- 直接調(diào)用成員變量時嗤疯,等號左邊是誰,就用誰的成員變量闺兢,沒有就向上找茂缚。
- 調(diào)用成員方法時,new的是誰的對象屋谭,就優(yōu)先用誰的方法脚囊,沒有就想上找,但是當在子類中找到該成員方法時桐磁,父類中必須要有該方法悔耘。
Fu obj = new Zi();
含義:受父類限制的子類對象,優(yōu)先執(zhí)行子類中復寫的父類方法所意,但是不能執(zhí)行父類中沒有的方法淮逊。
使用多態(tài)的好處
對象的上下轉(zhuǎn)型
向上轉(zhuǎn)型:
向上轉(zhuǎn)型就是類似于多態(tài)寫法,加入貓繼承于動物類扶踊,那么將貓看作動物就沒毛病,
創(chuàng)建格式::父類 obj = new 子類();
舉例:
Animal類:
public abstract class Animal {
public abstract void eat();
}
Cat 類:
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("貓吃魚");
}
// 子類特有方法
public void catchMouse() {
System.out.println("貓抓老鼠");
}
main方法:
public class Demo01Main {
public static void main(String[] args) {
// 對象的向上轉(zhuǎn)型郎任,就是:父類引用指向之類對象秧耗。
Animal animal = new Cat(); // 本來創(chuàng)建的時候是一只貓
animal.eat(); // 貓吃魚
// animal.catchMouse(); // 錯誤寫法!
但是這是再調(diào)用子類中特有的方法時舶治,就會報錯分井,因為這時對象已經(jīng)向上轉(zhuǎn)型,不能調(diào)用子類中父類沒有的方法霉猛。
向下轉(zhuǎn)型
對象的向下轉(zhuǎn)型其實是一個還原動作尺锚,類似于強制類型轉(zhuǎn)換。當貓類向上轉(zhuǎn)型到動物類后惜浅,再向下轉(zhuǎn)型為貓時瘫辩,沒問題。但是如果將這個之前是貓的動物類向下轉(zhuǎn)型為狗時坛悉,就不對了伐厌。
創(chuàng)建格式:子類名稱 對象名 = (子類名稱) 原本父類對象名;
含義:將父類對象還原為子類對象。
注意事項:在向下轉(zhuǎn)型之前裸影,必須保證該對象在創(chuàng)建時就是該子類的對象(貓)挣轨,這樣才能再次向下轉(zhuǎn)型為貓。
如果不是一個子類的話轩猩,就會報出ClassCastException的錯誤卷扮。
舉例:
Animal類:
public abstract class Animal {
public abstract void eat();
}
Cat類:
public class Cat extends Animal {
@Override
public void eat() {
System.out.println("貓吃魚");
}
// 子類特有方法
public void catchMouse() {
System.out.println("貓抓老鼠");
}
}
Dog類:
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃SHIT");
}
public void watchHouse() {
System.out.println("狗看家");
}
}
main類:
public class Demo01Main {
public static void main(String[] args) {
// 對象的向上轉(zhuǎn)型荡澎,就是:父類引用指向之類對象。
Animal animal = new Cat(); // 本來創(chuàng)建的時候是一只貓
animal.eat(); // 貓吃魚
// animal.catchMouse(); // 錯誤寫法晤锹!
// 向下轉(zhuǎn)型衔瓮,進行“還原”動作
Cat cat = (Cat) animal;
cat.catchMouse(); // 貓抓老鼠
// 下面是錯誤的向下轉(zhuǎn)型
// 本來new的時候是一只貓,現(xiàn)在非要當做狗
// 錯誤寫法抖甘!編譯不會報錯热鞍,但是運行會出現(xiàn)異常:
// java.lang.ClassCastException,類轉(zhuǎn)換異常
Dog dog = (Dog) animal;
}
}
分析:
- 在向上轉(zhuǎn)型后衔彻,父類對象animal不能調(diào)用子類Cat的特有方法薇宠,那么對他進行向下轉(zhuǎn)型,然后就可以調(diào)用了艰额。
- 將父類對象animal轉(zhuǎn)為子類Dog時澄港,編譯不會報錯,但是運行異常
java.lang.ClassCastException柄沮,類轉(zhuǎn)換異常
- 說明只能向下轉(zhuǎn)型為之前創(chuàng)建對象時的子類回梧。
類型判斷----instanceof關(guān)鍵字
在上一步中,我們是怎么知道該父類對象animal之前的引用是貓還是狗祖搓。
所以要進行類型的判斷狱意,使用instanceof關(guān)鍵字。
instanceof 嚴格來說是Java中的一個雙目運算符拯欧,用來測試一個對象是否為一個類的實例详囤,用法為:
對象名稱 instanceof 類名稱
實例:
/*
如何才能知道一個父類引用的對象,本來是什么子類镐作?
格式:
對象 instanceof 類名稱
這將會得到一個boolean值結(jié)果藏姐,也就是判斷前面的對象能不能當做后面類型的實例。
*/
public class Demo02Instanceof {
public static void main(String[] args) {
Animal animal = new Dog(); // 本來是一只狗
animal.eat(); // 狗吃SHIT
// 如果希望掉用子類特有方法该贾,需要向下轉(zhuǎn)型
// 判斷一下父類引用animal本來是不是Dog
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHouse(); //狗看家
}
// 判斷一下animal本來是不是Cat
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
}
// 調(diào)用給寵物的方法羔杨,傳進去Dog類對象
giveMeAPet(animal);
}
public static void giveMeAPet(Animal animal) {
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHouse();
}
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
}
}
}
分析:
- 首先創(chuàng)建Dog類對象,向上轉(zhuǎn)型為Animal類杨蛋,在使用時兜材,由于不知道該對象之前是哪個類的對象,所以要進行判斷該對象是什么類型的類對象六荒,使用instanceof關(guān)鍵字护姆,返回bool值,進而進行向下轉(zhuǎn)型掏击,就可以調(diào)用該子類中的特有方法了卵皂。
筆記本接口案例
- 筆記本電腦(laptop)通常具備使用USB設(shè)備的功能。在生產(chǎn)時砚亭,筆記本都預留了可以插入USB設(shè)備的USB接口灯变,
但具體是什么USB設(shè)備殴玛,筆記本廠商并不關(guān)心,只要符合USB規(guī)格的設(shè)備都可以添祸。 - 定義USB接口滚粟,具備最基本的開啟功能和關(guān)閉功能。鼠標和鍵盤要想能在電腦上使用刃泌,那么鼠標和鍵盤也必須遵守USB規(guī)范凡壤,實現(xiàn)USB接口,否則鼠標和鍵盤的生產(chǎn)出來也無法使用耙替。
分析
進行描述筆記本類亚侠,實現(xiàn)筆記本使用USB鼠標、USB鍵盤
- USB接口俗扇,包含開啟功能硝烂、關(guān)閉功能
- 筆記本類,包含運行功能铜幽、關(guān)機功能滞谢、使用USB設(shè)備功能
- 鼠標類,要實現(xiàn)USB接口除抛,并具備點擊的方法
- 鍵盤類狮杨,要實現(xiàn)USB接口,具備敲擊的方法
圖例:
代碼:
1.定義USB接口:
該接口有兩個抽象類--打開設(shè)備和關(guān)閉設(shè)備
public interface USB {
public abstract void open(); // 打開設(shè)備
public abstract void close(); // 關(guān)閉設(shè)備
}
2.定義鼠標類和(鍵盤類)
這兩個類繼承USB接口镶殷,實現(xiàn)接口中的抽象方法禾酱,并有獨有的動作方法--click()和type()
// 鼠標就是一個USB設(shè)備
public class Mouse implements USB {
@Override
public void open() {
System.out.println("打開鼠標");
}
@Override
public void close() {
System.out.println("關(guān)閉鼠標");
}
public void click() {
System.out.println("鼠標點擊");
}
}
// 鍵盤就是一個USB設(shè)備
public class Keyboard implements USB {
@Override
public void open() {
System.out.println("打開鍵盤");
}
@Override
public void close() {
System.out.println("關(guān)閉鍵盤");
}
public void type() {
System.out.println("鍵盤輸入");
}
}
3.定義電腦類
電腦類實現(xiàn)獨有方法--開機和關(guān)機。
定義使用USB設(shè)備的方法绘趋,該方法的參數(shù)類型為USB類型,在方法中
首先打開設(shè)備(執(zhí)行公有方法打開設(shè)備)颗管,判斷設(shè)備類型陷遮,執(zhí)行設(shè)備特有方法,關(guān)閉設(shè)備(執(zhí)行共有方法)
public class Computer {
public void powerOn() {
System.out.println("筆記本電腦開機");
}
public void powerOff() {
System.out.println("筆記本電腦關(guān)機");
}
// 使用USB設(shè)備的方法垦江,使用接口作為方法的參數(shù)
public void useDevice(USB usb) {
usb.open(); // 打開設(shè)備
if (usb instanceof Mouse) { // 一定要先判斷
Mouse mouse = (Mouse) usb; // 向下轉(zhuǎn)型
mouse.click();
} else if (usb instanceof Keyboard) { // 先判斷
Keyboard keyboard = (Keyboard) usb; // 向下轉(zhuǎn)型
keyboard.type();
}
usb.close(); // 關(guān)閉設(shè)備
}
}
Main:
public class DemoMain {
public static void main(String[] args) {
// 首先創(chuàng)建一個筆記本電腦
Computer computer = new Computer();
computer.powerOn();
// 準備一個鼠標帽馋,供電腦使用
// Mouse mouse = new Mouse();
// 首先進行向上轉(zhuǎn)型
USB usbMouse = new Mouse(); // 多態(tài)寫法
// 參數(shù)是USB類型,我正好傳遞進去的就是USB鼠標
computer.useDevice(usbMouse);
// 創(chuàng)建一個USB鍵盤
Keyboard keyboard = new Keyboard(); // 沒有使用多態(tài)寫法
// 方法參數(shù)是USB類型比吭,傳遞進去的是實現(xiàn)類對象
computer.useDevice(keyboard); // 正確寫法绽族!也發(fā)生了向上轉(zhuǎn)型
// 使用子類對象,匿名對象衩藤,也可以
// computer.useDevice(new Keyboard()); // 也是正確寫法
computer.powerOff();
System.out.println("==================");
method(10.0); // 正確寫法吧慢,double --> double
method(20); // 正確寫法,int --> double
int a = 30;
method(a); // 正確寫法赏表,int --> double
}
public static void method(double num) {
System.out.println(num);
}
}
Main函數(shù)分析:
- 首先創(chuàng)建一個筆記本類检诗,執(zhí)行筆記本的開機方法(powerOn)匈仗。
- 接入鼠標時,使用多態(tài)寫法將鼠標類向上轉(zhuǎn)型為USB接口類創(chuàng)建對象逢慌,將該對象傳入電腦類的USB使用方法中(useDevice)悠轩,這是常規(guī)寫法,符合要求攻泼,進而在該方法中執(zhí)行鼠標類中實現(xiàn)USB接口類的打開設(shè)備和關(guān)閉設(shè)備的兩個方法火架,并判斷類型為鼠標類,執(zhí)行獨有方法--點擊(click)忙菠。
- 接入鍵盤何鸡,不在顯式的將鍵盤類向上轉(zhuǎn)為USB接口類,采用正常寫法創(chuàng)建鍵盤類對象只搁,進而調(diào)用電腦類的使用USB的方法音比,將鍵盤對象傳進去,執(zhí)行該方法氢惋。(與鼠標執(zhí)行一致)
- 第三部也是正確的洞翩,原因就是發(fā)生了類似于自動類型轉(zhuǎn)換的情況,例如:定義一個接受double類型參數(shù)的方法焰望,當傳入int類型時骚亿,也是可以的,發(fā)生自動類型轉(zhuǎn)換熊赖,將int轉(zhuǎn)為double類型来屠。
4.關(guān)閉電腦--執(zhí)行powerOff方法。