看過一些關於Java多態性的文章,參考了不少人的理解,加入了一些本身的見解,整理出來供你們參考,不必定徹底正確,歡迎你們批評指正。java
(一)相關類web
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...{}
(二)問題:如下輸出結果是什麼?字體
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)); ⑨
(三)答案this
① A and Aspa
② A and A設計
③ A and Dcode
④ B and Aorm
⑤ B and A對象
⑥ A and D繼承
⑦ B and B
⑧ B and B
⑨ A and D
(四)分析
①②③比較好理解,通常不會出錯。④⑤就有點糊塗了,爲何輸出的不是"B and B」呢?!!先來回顧一下多態性。
運行時多態性是面向對象程序設計代碼重用的一個最強大機制,Java多態性的概念也能夠被說成「一個接口,多個方法」。Java實現運行時多態性的基礎是動態方法調度,它是一種在運行時而不是在編譯期調用重載方法的機制。
方法的重寫Overriding和重載Overloading是Java多態性的不一樣表現。重寫Overriding是父類與子類之間多態性的一種表現,重載Overloading是一個類中多態性的一種表現。若是在子類中定義某方法與其父類有相同的名稱和參數,咱們說該方法被重寫(Overriding)。子類的對象使用這個方法時,將調用子類中的定義,對它而言,父類中的定義如同被「屏蔽」了。若是在一個類中定義了多個同名的方法,它們或有不一樣的參數個數或有不一樣的參數類型,則稱爲方法的重載(Overloading)。Overloaded的方法是能夠改變返回值的類型。方法的重寫Overriding和重載Overloading是Java多態性的不一樣表現。重寫Overriding是父類與子類之間多態性的一種表現,重載Overloading是一個類中Java多態性的一種表現。若是在子類中定義某方法與其父類有相同的名稱和參數,咱們說該方法被重寫 (Overriding)。子類的對象使用這個方法時,將調用子類中的定義,對它而言,父類中的定義如同被「屏蔽」了。若是在一個類中定義了多個同名的方法,它們或有不一樣的參數個數或有不一樣的參數類型,則稱爲方法的重載(Overloading)。Overloaded的方法是能夠改變返回值的類型。
當超類對象引用變量引用子類對象時,被引用對象的類型而不是引用變量的類型決定了調用誰的成員方法,可是這個被調用的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。 (可是若是強制把超類轉換成子類的話,就能夠調用子類中新添加而超類沒有的方法了。)
好了,先溫習到這裏,言歸正傳!實際上這裏涉及方法調用的優先問題 ,優先級由高到低依次爲:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。讓咱們來看看它是怎麼工做的。
好比④,a2.show(b),a2是一個引用變量,類型爲A,則this爲a2,b是B的一個實例,因而它到類A裏面找show(B obj)方法,沒有找到,因而到A的super(超類)找,而A沒有超類,所以轉到第三優先級this.show((super)O),this仍然是a2,這裏O爲B,(super)O即(super)B即A,所以它到類A裏面找show(A obj)的方法,類A有這個方法,可是因爲a2引用的是類B的一個對象,B覆蓋了A的show(A obj)方法,所以最終鎖定到類B的show(A obj),輸出爲"B and A」。
再好比⑧,b.show(c),b是一個引用變量,類型爲B,則this爲b,c是C的一個實例,因而它到類B找show(C obj)方法,沒有找到,轉而到B的超類A裏面找,A裏面也沒有,所以也轉到第三優先級this.show((super)O),this爲b,O爲C,(super)O即(super)C即B,所以它到B裏面找show(B obj)方法,找到了,因爲b引用的是類B的一個對象,所以直接鎖定到類B的show(B obj),輸出爲"B and B」。
按照上面的方法,能夠正確獲得其餘的結果。
問題還要繼續,如今咱們再來看上面的分析過程是怎麼體現出藍色字體那句話的內涵的。它說:當超類對象引用變量引用子類對象時,被引用對象的類型而不是引用變量的類型決定了調用誰的成員方法,可是這個被調用的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。仍是拿a2.show(b)來講吧。
a2是一個引用變量,類型爲A,它引用的是B的一個對象,所以這句話的意思是由B來決定調用的是哪一個方法。所以應該調用B的show(B obj)從而輸出"B and B」纔對。可是爲何跟前面的分析獲得的結果不相符呢?!問題在於咱們不要忽略了藍色字體的後半部分,那裏特別指明:這個被調用的方法必須是在超類中定義過的,也就是被子類覆蓋的方法。B裏面的show(B obj)在超類A中有定義嗎?沒有!那就更談不上被覆蓋了。實際上這句話隱藏了一條信息:它仍然是按照方法調用的優先級來肯定的。它在類A中找到了show(A obj),若是子類B沒有覆蓋show(A obj)方法,那麼它就調用A的show(A obj)(因爲B繼承A,雖然沒有覆蓋這個方法,但從超類A那裏繼承了這個方法,從某種意義上說,仍是由B肯定調用的方法,只是方法是在A中實現而已);如今子類B覆蓋了show(A obj),所以它最終鎖定到B的show(A obj)。這就是那句話的意義所在,到這裏,咱們能夠清晰的理解Java的多態性了。