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