JAVA 多態(tài) 由淺及深介紹
什么是多態(tài)?
多態(tài)分兩種:
(1) 編譯時(shí)多態(tài)(設(shè)計(jì)時(shí)多態(tài)):方法重載。
(2) 運(yùn)行時(shí)多態(tài):JAVA運(yùn)行時(shí)系統(tǒng)根據(jù)調(diào)用該方法的實(shí)例的類型來(lái)決定選擇調(diào)用哪個(gè)方法則被稱為運(yùn)行時(shí)多態(tài)。(我們平時(shí)說(shuō)得多的事運(yùn)行時(shí)多態(tài),所以多態(tài)主要也是指運(yùn)行時(shí)多態(tài))
運(yùn)行時(shí)多態(tài)存在的三個(gè)必要條件:
一、要有繼承(包括接口的實(shí)現(xiàn));
二、要有重寫(xiě);
三、父類引用指向子類對(duì)象。
--------------------------------------------------------------------------------
詳細(xì)解釋:
運(yùn)行時(shí)多態(tài)的解釋:a.運(yùn)行時(shí)多態(tài)是指程序中定義的引用變量所指向的具體類型和b.通過(guò)該引用變量發(fā)出的方法調(diào)用在編程時(shí)并不確定,而是在程序運(yùn)行期間才確定,即一個(gè)引用變量倒底會(huì)指向哪個(gè)類的實(shí)例對(duì)象,該引用變量發(fā)出的方法調(diào)用到底是哪個(gè)類中實(shí)現(xiàn)的方法,必須在由程序運(yùn)行期間才能決定.
1.程序序中定義的引用變量所指向的具體類型不確定(即一個(gè)引用變量倒底會(huì)指向哪個(gè)類的實(shí)例對(duì)象) 。
例子 :
driver 類中 drive 方法 (Vehicle類 vehicle){}
•oneDriver.drive( new car() )
•oneDriver.drive( new bus() )
其中vehicle 變量無(wú)法確定具體使用哪個(gè)子類實(shí)例。
1.通過(guò)該引用變量發(fā)出的方法調(diào)用在編程時(shí)并不確定(該引用變量發(fā)出的方法調(diào)用到底是哪個(gè)類中實(shí)現(xiàn)的方法) 。
例子 : 廚師,園丁,理發(fā)師的Cut 方法調(diào)用.persion.cut().
--------------------------------------------------------------------------------
多態(tài)的好處:
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)尤為突出和重要。
實(shí)際運(yùn)用:
結(jié)合配置文件的使用,聯(lián)系Spring框架,利用反射,動(dòng)態(tài)的調(diào)用類,同時(shí)不用修改源代碼,直接添加新類和修改配置文件,不需要重啟服務(wù)器便可以擴(kuò)展程序。
--------------------------------------------------------------------------------
小結(jié):
使用父類類型的引用指向子類的對(duì)象,該引用調(diào)用的師父類中定義的方法和變量,變量不能被重寫(xiě)(覆蓋);如果子類中重寫(xiě)了父類中的一個(gè)方法,那么在調(diào)用這個(gè)方法的時(shí)候,將會(huì)調(diào)用子類中的這個(gè)方法;
注意特殊情況,如果該父類引用所調(diào)用的方法參數(shù)列表未定義,就調(diào)用該父類的父類中查找,如果還沒(méi)找到就強(qiáng)制向上類型轉(zhuǎn)換參數(shù)列表中的參數(shù)類型,具體優(yōu)先級(jí)高到低依次如下:
this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。
--------------------------------------------------------------------------------
經(jīng)典筆試題(混合重載和重寫(xiě)):
(一)相關(guān)類
class A {
public String show(D obj)...{
return ("A and D");
}
public String show(A obj)...{
return ("A and A");
}
}
class B extends A{
public String show(B obj)...{
return ("B and B");
}
public String show(A obj)...{
return ("B and A");
}
}
class C extends B...{}
class D extends B...{}
(二)問(wèn)題:以下輸出結(jié)果是什么?
A a1 = new A();
A a2 = new B();
B b = new B();
C c = new C();
D d = new D();
System.out.println(a1.show(b)); ①
System.out.println(a1.show(c)); ②
System.out.println(a1.show(d)); ③
System.out.println(a2.show(b)); ④
System.out.println(a2.show(c)); ⑤
System.out.println(a2.show(d)); ⑥
System.out.println(b.show(b)); ⑦
System.out.println(b.show(c)); ⑧
System.out.println(b.show(d)); ⑨
(三)答案
① A and A
② A and A
③ A and D
④ B and A
⑤ B and A
⑥ A and D
⑦ B and B
⑧ B and B
⑨ A and D
(四)分析
①②③比較好理解,一般不會(huì)出錯(cuò)。④⑤就有點(diǎn)糊涂了,為什么輸出的不是"B and B”呢?!!先來(lái)回顧一下多態(tài)性。
運(yùn)行時(shí)多態(tài)性是面向?qū)ο蟪绦蛟O(shè)計(jì)代碼重用的一個(gè)最強(qiáng)大機(jī)制,動(dòng)態(tài)性的概念也可以被說(shuō)成“一個(gè)接口,多個(gè)方法”。Java實(shí)現(xiàn)運(yùn)行時(shí)多態(tài)性的基礎(chǔ)是動(dòng)態(tài)方法調(diào)度,它是一種在運(yùn)行時(shí)而不是在編譯期調(diào)用重載方法的機(jī)制。
方法的重寫(xiě)Overriding和重載Overloading是Java多態(tài)性的不同表現(xiàn)。重寫(xiě)Overriding是父類與子類之間多態(tài)性的一種表現(xiàn),重載Overloading是一個(gè)類中多態(tài)性的一種表現(xiàn)。如果在子類中定義某方法與其父類有相同的名稱和參數(shù),我們說(shuō)該方法被重寫(xiě)(Overriding)。子類的對(duì)象使用這個(gè)方法時(shí),將調(diào)用子類中的定義,對(duì)它而言,父類中的定義如同被“屏蔽”了。如果在一個(gè)類中定義了多個(gè)同名的方法,它們或有不同的參數(shù)個(gè)數(shù)或有不同的參數(shù)類型,則稱為方法的重載(Overloading)。Overloaded的方法是可以改變返回值的類型但同時(shí)參數(shù)列表也得不同。
當(dāng)超類對(duì)象引用變量引用子類對(duì)象時(shí),被引用對(duì)象的類型而不是引用變量的類型決定了調(diào)用誰(shuí)的成員方法,但是這個(gè)被調(diào)用的方法必須是在超類中定義過(guò)的,也就是說(shuō)被子類覆蓋的方法。 (但是如果強(qiáng)制把超類轉(zhuǎn)換成子類的話,就可以調(diào)用子類中新添加而超類沒(méi)有的方法了。)
好了,先溫習(xí)到這里,言歸正傳!實(shí)際上這里涉及方法調(diào)用的優(yōu)先問(wèn)題 ,優(yōu)先級(jí)由高到低依次為:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。讓我們來(lái)看看它是怎么工作的。
比如④,a2.show(b),a2是一個(gè)引用變量,類型為A,則this為a2,b是B的一個(gè)實(shí)例,于是它到類A里面找show(B obj)方法,沒(méi)有找到,于是到A的super(超類)找,而A沒(méi)有超類,因此轉(zhuǎn)到第三優(yōu)先級(jí)this.show((super)O),this仍然是a2,這里O為B,(super)O即(super)B即A,因此它到類A里面找show(A obj)的方法,類A有這個(gè)方法,但是由于a2引用的是類B的一個(gè)對(duì)象,B覆蓋了A的show(A obj)方法,因此最終鎖定到類B的show(A obj),輸出為"B and A”。
再比如⑧,b.show(c),b是一個(gè)引用變量,類型為B,則this為b,c是C的一個(gè)實(shí)例,于是它到類B找show(C obj)方法,沒(méi)有找到,轉(zhuǎn)而到B的超類A里面找,A里面也沒(méi)有,因此也轉(zhuǎn)到第三優(yōu)先級(jí)this.show((super)O),this為b,O為C,(super)O即(super)C即B,因此它到B里面找show(B obj)方法,找到了,由于b引用的是類B的一個(gè)對(duì)象,因此直接鎖定到類B的show(B obj),輸出為"B and B”。
按照上面的方法,可以正確得到其他的結(jié)果。
問(wèn)題還要繼續(xù),現(xiàn)在我們?cè)賮?lái)看上面的分析過(guò)程是怎么體現(xiàn)出藍(lán)色字體那句話的內(nèi)涵的。它說(shuō):當(dāng)超類對(duì)象引用變量引用子類對(duì)象時(shí),被引用對(duì)象的類型而不是引用變量的類型決定了調(diào)用誰(shuí)的成員方法,但是這個(gè)被調(diào)用的方法必須是在超類中定義過(guò)的,也就是說(shuō)被子類覆蓋的方法。還是拿a2.show(b)來(lái)說(shuō)吧。
a2是一個(gè)引用變量,類型為A,它引用的是B的一個(gè)對(duì)象,因此這句話的意思是由B來(lái)決定調(diào)用的是哪個(gè)方法。因此應(yīng)該調(diào)用B的show(B obj)從而輸出"B and B”才對(duì)。但是為什么跟前面的分析得到的結(jié)果不相符呢?!問(wèn)題在于我們不要忽略了藍(lán)色字體的后半部分,那里特別指明:這個(gè)被調(diào)用的方法必須是在超類中定義過(guò)的,也就是被子類覆蓋的方法。B里面的show(B obj)在超類A中有定義嗎?沒(méi)有!那就更談不上被覆蓋了。實(shí)際上這句話隱藏了一條信息:它仍然是按照方法調(diào)用的優(yōu)先級(jí)來(lái)確定的。它在類A中找到了show(A obj),如果子類B沒(méi)有覆蓋show(A obj)方法,那么它就調(diào)用A的show(A obj)(由于B繼承A,雖然沒(méi)有覆蓋這個(gè)方法,但從超類A那里繼承了這個(gè)方法,從某種意義上說(shuō),還是由B確定調(diào)用的方法,只是方法是在A中實(shí)現(xiàn)而已);現(xiàn)在子類B覆蓋了show(A obj),因此它最終鎖定到B的show(A obj)。這就是那句話的意義所在。
相關(guān)文章
springboot動(dòng)態(tài)加載Echarts柱狀圖
這篇文章主要為大家詳細(xì)介紹了springboot動(dòng)態(tài)加載Echarts柱狀圖,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-12-12Java?hibernate延遲加載get和load的區(qū)別
這篇文章主要介紹了Java?hibernate延遲加載get和load的區(qū)別,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下2022-09-09MyBatis-Plus里面的增刪改查詳解(化繁為簡(jiǎn))
這篇文章主要給大家介紹了關(guān)于MyBatis-Plus里面的增刪改查的相關(guān)資料,Mybatis-Plus是一個(gè)基于Mybatis的增強(qiáng)工具,可以簡(jiǎn)化Mybatis的開(kāi)發(fā),提高開(kāi)發(fā)效率,需要的朋友可以參考下2023-07-07JavaMail入門(mén)教程之創(chuàng)建郵件(2)
這篇文章主要介紹了JavaMail入門(mén)教程之創(chuàng)建郵件的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-11-11springboot配置文件中使用${}注入值的兩種方式小結(jié)
這篇文章主要介紹了springboot配置文件中使用${}注入值的兩種方式小結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03mybatis框架order by作為參數(shù)傳入時(shí)失效的解決
這篇文章主要介紹了mybatis框架order by作為參數(shù)傳入時(shí)失效的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06springmvc級(jí)聯(lián)屬性處理無(wú)法轉(zhuǎn)換異常問(wèn)題解決
這篇文章主要介紹了springmvc級(jí)聯(lián)屬性處理無(wú)法轉(zhuǎn)換異常問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12