目錄
- 1. 概述
- 定義
- 前提條件
- 2. 多型的體現
- 3. 多型的好處
- 4. 參考型別轉換
- 向上轉型
- 向下轉型
- 為什么要轉型
- 轉型的例外
- instanceof 關鍵字
- 5. 多型的弊端
- 6. 介面多型案例
- 案例需求
- 代碼實作
1. 概述
生活中,比如跑的動作,小貓、小狗和大象,跑起來是不一樣的,再比如飛的動作,昆蟲、鳥類和飛機,飛起來也是不一樣的,可見,同一行為,通過不同的事物,可以體現出來的不同的形態,多型,描述的就是這樣的狀態,
定義
- 多型: 是指對同一方法的呼叫,由于編譯時型別和運行時型別不一致可能會有不同的行為
前提條件
- 繼承或者實作【二選一】
- 方法的重寫【意義體現:不重寫,無意義】
- 父類參考指向子類物件【格式體現】
2. 多型的體現
多型體現的格式:
父型別別 變數名 = 子類物件;
變數名.方法名();
父型別別:指子類物件繼承的父型別別(包括抽象類),或者實作的父介面型別,
相應的就有三種多型形式:普通類多型、抽象類多型、介面多型,
代碼如下:
Fu f = new Zi();
f.method();
父型別別的變數可以指向子型別別的物件,記憶體中創建的還是子類的物件,呼叫的方法是子類的實作;
同一個父類的方法可以被多個子類重寫,呼叫的是各個子類重寫后的方法,一個參考型別變數如果宣告為父類的型別,但實際參考的是子類物件,那么該變數就不能再訪問子類中添加的屬性和方法,
當使用多型方式呼叫方法時,首先檢查父類中是否有該方法,如果沒有,則編譯不通過;如果有,執行的是子類重寫后的方法
代碼如下:
定義父類:
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) {
// 抽象類多型形式,創建物件,抽象類參考指向子類物件
Animal a1 = new Cat();
// 呼叫的是 Cat 的 eat方法
a1.eat();
// 抽象類多型形式,創建物件,抽象類參考指向子類物件
Animal a2 = new Dog();
// 呼叫的是 Dog 的 eat方法
a2.eat();
}
}
多型指的是方法的多型,成員變數沒有多型的概念:
-
多型下的成員變數:
- 編譯時期:檢查父型別中是否有被呼叫的成員變數,沒有則編譯失敗,
- 運行時期:呼叫父型別中的成員變數,
-
多型下的成員方法:
- 編譯時期:檢查父型別中是否有被呼叫的成員方法,沒有則編譯失敗,
- 運行時期:運行子類(實作類)重寫后的方法,
3. 多型的好處
實際開發的程序中,父型別別作為方法形式引數,傳遞子類物件給方法,進行方法的呼叫,更能體現出多型的擴展性與便利,代碼如下:
定義父類:
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物件 傳遞給showAnimalEat方法,
當eat方法執行時,執行的是子類重寫的方法,那么效果自然與showCatEat、showDogEat方法一致,所以showAnimalEat完全可以替代以上兩方法,
不僅僅是替代,在擴展性方面,無論之后再多的子類出現,我們都不需要撰寫showXxxEat方法了,直接使用showAnimalEat都可以完成,
所以,多型的好處,體現在,可以簡化程式代碼,并有良好的擴展性,
4. 參考型別轉換
多型的轉型分為向上轉型與向下轉型兩種:
向上轉型
- 向上轉型:多型本身是子型別別向父型別別向上轉換的程序,這個程序是默認的,
當父類參考指向一個子類物件時,便是向上轉型,
使用格式:
父型別別 變數名 = 子類物件;
如:Animal a = new Cat();
向下轉型
- 向下轉型:父型別別向子型別別向下轉換的程序,這個程序是強制的,
一個已經向上轉型的子類物件,將父類參考轉為子類參考,可以使用強制型別轉換的格式,便是向下轉型,
使用格式:
子型別別 變數名 = (子型別別) 父類變數名;
如:Cat c =(Cat) a;
為什么要轉型
當使用多型方式呼叫方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤,也就是說,不能呼叫子類擁有 而父類沒有的方法,編譯都錯誤,更別說運行了,所以,想要呼叫子類特有的方法,必須做向下轉型,
轉型演示,代碼如下:
定義類:
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()
}
}
物件的上下轉型原理:

轉型的例外
轉型的程序中,一不小心就會遇到這樣的問題,請看如下代碼:
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物件的,這兩個型別并沒有任何繼承關系,不符合型別轉換的定義,
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()
}
}
}
5. 多型的弊端
向上轉型只能使用父類共性的內容,而無法使用子類特有功能,功能有限制;當要使用子類特有功能時,就需要進行向下轉型,且容易發生ClassCastException型別轉換例外,在轉換之前必須做型別判斷,
6. 介面多型案例
案例需求
筆記本電腦通常具備使用USB設備的功能,在生產時,筆記本都預留了可以插入USB設備的USB介面,但具體是什么USB設備,筆記本廠商并不關心,只要符合USB規范的設備都可以,
定義USB介面,具備最基本的開啟功能和關閉功能,滑鼠和鍵盤要想能在電腦上使用,那么滑鼠和鍵盤也必須遵守USB規范,實作USB介面,否則滑鼠和鍵盤的生產出來也無法使用,
-
USB介面,包含開啟功能、關閉功能
-
筆記本類,包含運行功能、關機功能、使用USB設備功能
-
滑鼠類,要符合USB介面,并具備點擊的方法
-
鍵盤類,要符合USB介面,并具備敲擊的方法
代碼實作
定義USB介面:
//定義滑鼠、鍵盤,筆記本三者之間應該遵守的規則
public interface USB {
void open();// 開啟功能
void close();// 關閉功能
}
定義滑鼠類:
//滑鼠實作USB規則
public class Mouse implements USB {
public void open() {
System.out.println("滑鼠開啟");
}
public void close() {
System.out.println("滑鼠關閉");
}
public void click() {
System.out.println("滑鼠點擊");
}
}
定義鍵盤類:
//鍵盤實作USB規則
public class KeyBoard implements USB {
public void open() {
System.out.println("鍵盤開啟");
}
public void close() {
System.out.println("鍵盤關閉");
}
public void type () {
System.out.println("鍵盤打字");
}
}
定義筆記本類:
//定義筆記本
public class NoteBook {
// 筆記本開啟運行功能
public void run() {
System.out.println("筆記本運行");
}
// 筆記本使用usb設備,這時當筆記本物件呼叫這個功能時,必須給其傳遞一個符合USB規則的USB設備
public void useUSB(USB usb) {
// 判斷是否有USB設備
if (usb != null) {
usb.open();
// 型別轉換,呼叫特有方法
if (usb instanceof Mouse) {
Mouse m = (Mouse) usb;
m.click();
} else if (usb instanceof KeyBoard) {
KeyBoard kb = (KeyBoard) usb;
kb.type();
}
usb.close();
}
}
public void shutDown() {
System.out.println("筆記本關閉");
}
}
測驗類:
public class Test {
public static void main(String[] args) {
// 創建筆記本物件
NoteBook nb = new NoteBook();
// 筆記本開啟
nb.run();
// 創建滑鼠物件
Mouse m = new Mouse();
// 筆記本使用滑鼠
nb.useUSB(m);
// 創建鍵盤物件
KeyBoard kb = new KeyBoard();
// 筆記本使用鍵盤
nb.useUSB(kb);
// 筆記本關閉
nb.shutDown();
}
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/49411.html
標籤:Java
上一篇:goweb-錯誤處理,除錯和測驗
