
多肽核心機(jī)制
1.面向?qū)ο笞詈诵牡臋C(jī)制——動態(tài)綁定,也叫多態(tài)
2.多態(tài)就是指程序中定義的引用變量所指向的具體類型和通過該引用變量發(fā)出的方法調(diào)用在編譯時并不確定,而是在程序運(yùn)行期間才確定,即一個引用變量倒底會指向哪個類的實例對象,該引用變量發(fā)出的方法調(diào)用到底是哪個類中實現(xiàn)的方法,必須在由程序運(yùn)行期間才能決定。因為在程序運(yùn)行時才確定具體的類,這樣,不用修改源程序代碼,就可以讓引用變量綁定到各種不同的類實現(xiàn)上,從而導(dǎo)致該引用調(diào)用的具體方法隨之改變,即不修改程序代碼就可以改變程序運(yùn)行時所綁定的具體代碼,讓程序可以選擇多個運(yùn)行狀態(tài),這就是多態(tài)性。這個跟iOS中的運(yùn)行時runtime運(yùn)行時具有類似的原理。
3.指向子類的父類引用由于向上轉(zhuǎn)型了,它只能訪問父類中擁有的方法和屬性,而對于子類中存在而父類中不存在的方法,該引用是不能使用的,盡管是重載該方法。若子類重寫了父類中的某些方法,在調(diào)用該些方法的時候,必定是使用子類中定義的這些方法(動態(tài)連接、動態(tài)調(diào)用)
4.在繼承鏈中對象方法的調(diào)用存在一個優(yōu)先級:this.Display(object)、super.Display(object)、this.Display((super)object)、super.Display((super)object)。
多肽的定義
指允許不同類的對象對同一消息做出響應(yīng)。即同一消息可以根據(jù)發(fā)送對象的不同而采用多種不同的行為方式。(發(fā)送消息就是函數(shù)調(diào)用)
多態(tài)存在的三個必要條件
- 要有繼承;
- 要有重寫;
- 父類引用指向子類對象。
為什么要用多肽,好處是哪些?
我們都知道,一個事物的產(chǎn)生必然有其存在的道理,很多都是為了解決一些難題而產(chǎn)生,那么多肽具有哪些有點呢。
- 1.可替換性(substitutability)。多態(tài)對已存在代碼具有可替換性。例如,多態(tài)對圓Circle類工作,對其他任何圓形幾何體,如圓環(huán),也同樣工作。
- 2.可擴(kuò)充性(extensibility)。多態(tài)對代碼具有可擴(kuò)充性。增加新的子類不影響已存在類的多態(tài)性、繼承性,以及其他特性的運(yùn)行和操作。實際上新加子類更容易獲得多態(tài)功能。例如,在實現(xiàn)了圓錐、半圓錐以及半球體的多態(tài)基礎(chǔ)上,很容易增添球體類的多態(tài)性。
- 3.接口性(interface-ability)。多態(tài)是超類通過方法簽名,向子類提供了一個共同接口,由子類來完善或者覆蓋它而實現(xiàn)的。如圖8.3所示。圖中超類Shape規(guī)定了兩個實現(xiàn)多態(tài)的接口方法,computeArea()以及computeVolume()。子類,如Circle和Sphere為了實現(xiàn)多態(tài),完善或者覆蓋這兩個接口方法。
- 4.靈活性(flexibility)。它在應(yīng)用中體現(xiàn)了靈活多樣的操作,提高了使用效率。
- 5.簡化性(simplicity)。多態(tài)簡化對應(yīng)用軟件的代碼編寫和修改過程,尤其在處理大量對象的運(yùn)算和操作時,這個特點尤為突出和重要。
代碼帶你認(rèn)識多肽
多態(tài)體現(xiàn)的格式:
父類類型 變量名 = new 子類對象;
變量名.方法名();
父類類型:指子類對象繼承的父類類型,或者實現(xiàn)的父接口
Fu f = new Zi();
f.method();
當(dāng)使用多態(tài)方式調(diào)用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤;如果有,執(zhí)行的是子類重寫后方法。
- 代碼如下:
//定義父類
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) {
// 多態(tài)形式,創(chuàng)建對象
Animal a1 = new Cat();
// 調(diào)用的是 Cat 的 eat
a1.eat();
// 多態(tài)形式,創(chuàng)建對象
Animal a2 = new Dog();
// 調(diào)用的是 Dog 的 eat
a2.eat();
}
}
代碼告訴你多肽的好處
實際開發(fā)的過程中,父類類型作為方法形式參數(shù),傳遞子類對象給方法,進(jìn)行方法的調(diào)用,更能體現(xiàn)出多態(tài)的擴(kuò)展 性與便利。代碼如下:
//定義父類
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) {
// 多態(tài)形式,創(chuàng)建對象
Cat c = new Cat();
Dog d = new Dog();
// 調(diào)用showCatEat
showCatEat(c);
// 調(diào)用showDogEat
showDogEat(d);
/*
以上兩個方法, 均可以被showAnimalEat(Animal a)方法所替代
而執(zhí)行效果一致
*/
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();
}
}
由于多態(tài)特性的支持,showAnimalEat方法的Animal類型,是Cat和Dog的父類類型,父類類型接收子類對象,當(dāng)然可以把Cat對象和Dog對象,傳遞給方法。
當(dāng)eat方法執(zhí)行時,多態(tài)規(guī)定,執(zhí)行的是子類重寫的方法,那么效果自然與showCatEat、showDogEat方法一致,所以showAnimalEat完全可以替代以上兩方法。
不僅僅是替代,在擴(kuò)展性方面,無論之后再多的子類出現(xiàn),我們都不需要編寫showXxxEat方法了,直接使用showAnimalEat都可以完成。
所以,多態(tài)的好處,體現(xiàn)在,可以使程序編寫的更簡單,并有良好的擴(kuò)展
引用類型轉(zhuǎn)換
多態(tài)的轉(zhuǎn)型分為向上轉(zhuǎn)型與向下轉(zhuǎn)型兩種:
向上轉(zhuǎn)型
- 向上轉(zhuǎn)型:多態(tài)本身是子類類型向父類類型向上轉(zhuǎn)換的過程,這個過程是默認(rèn)的。
當(dāng)父類引用指向一個子類對象時,便是向上轉(zhuǎn)型。
使用格式:
父類類型 變量名 = new 子類類型();
如:Animal a = new Cat();
向下轉(zhuǎn)型
- 向下轉(zhuǎn)型:父類類型向子類類型向下轉(zhuǎn)換的過程,這個過程是強(qiáng)制的
一個已經(jīng)向上轉(zhuǎn)型的子類對象,將父類引用轉(zhuǎn)為子類引用,可以使用強(qiáng)制類型轉(zhuǎn)換的格式,便是向下轉(zhuǎn)型。
使用格式:
子類類型 變量名 = (子類類型) 父類變量名;
如:Cat c =(Cat) a;
為什么要轉(zhuǎn)型
當(dāng)使用多態(tài)方式調(diào)用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤。也就是說,不能調(diào)用子類擁
有,而父類沒有的方法。編譯都錯誤,更別說運(yùn)行了。這也是多態(tài)給我們帶來的一點"小麻煩"。所以,想要調(diào)用子
類特有的方法,必須做向下轉(zhuǎn)型。
- 轉(zhuǎn)型演示,代碼如下:
//定義類
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) {
// 向上轉(zhuǎn)型
Animal a = new Cat();
a.eat(); // 調(diào)用的是 Cat 的 eat
// 向下轉(zhuǎn)型
Cat c = (Cat)a;
c.catchMouse(); // 調(diào)用的是 Cat 的 catchMouse
}
}
轉(zhuǎn)型的異常
轉(zhuǎn)型的過程中,一不小心就會遇到這樣的問題,請看如下代碼:
public class Test {
public static void main(String[] args) {
// 向上轉(zhuǎn)型
Animal a = new Cat();
a.eat(); // 調(diào)用的是 Cat 的 eat
// 向下轉(zhuǎn)型
Dog d = (Dog)a;
d.watchHouse(); // 調(diào)用的是 Dog 的 watchHouse 【運(yùn)行報錯】
}
}
這段代碼可以通過編譯,但是運(yùn)行時,卻報出了 ClassCastException ,類型轉(zhuǎn)換異常!這是因為,明明創(chuàng)建了Cat類型對象,運(yùn)行時,當(dāng)然不能轉(zhuǎn)換成Dog對象的。這兩個類型并沒有任何繼承關(guān)系,不符合類型轉(zhuǎn)換的定義。為了避免ClassCastException的發(fā)生,Java提供了 instanceof 關(guān)鍵字,給引用變量做類型的校驗,格式如下:
變量名 instanceof 數(shù)據(jù)類型
如果變量屬于該數(shù)據(jù)類型,返回true。
如果變量不屬于該數(shù)據(jù)類型,返回false。
所以,轉(zhuǎn)換前,我們最好先做一個判斷,代碼如下:
public class Test {
public static void main(String[] args) {
// 向上轉(zhuǎn)型
Animal a = new Cat();
a.eat(); // 調(diào)用的是 Cat 的 eat
// 向下轉(zhuǎn)型
if (a instanceof Cat){
Cat c = (Cat)a;
c.catchMouse(); // 調(diào)用的是 Cat 的 catchMouse
} else if (a instanceof Dog){
Dog d = (Dog)a;
d.watchHouse(); // 調(diào)用的是 Dog 的 watchHouse
}
}
}