原作者thinkGhoster,鏈接
http://blog.csdn.net/thinkGhoster/archive/2008/04/19/2307001.aspx
對原文進行了適當的調整,更適合閱讀。
注:本人讀后的一點思考在文章最后。
(一)相關類

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)); ⑨
(三)答案
① 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
(四)分析
①②③比較好理解,一般不會出錯。④⑤就有點糊涂了,為什么輸出的不是"B and B”呢?!!先來回顧一下多態性。
運行時多態性是面向對象程序設計代碼重用的一個最強大機制,動態性的概念也可以被說成“一個接口,多個方法”。Java實現運行時多態性的基礎是動態方法調度,它是一種在運行時而不是在編譯期調用重載方法的機制。
方法的重寫Overriding和重載Overloading是Java多態性的不同表現。
- 重寫(Overriding) 是父類與子類之間多態性的一種表現。如果在子類中定義某方法與其父類有相同的名稱和參數,我們說該方法被重寫
(Overriding)。子類的對象使用這個方法時,將調用子類中的定義,對它而言,父類中的定義如同被“屏蔽”了。
- 重載(Overloading) 是一個類中多態性的一種表現。如果在一個類中定義了多個同名的方法,它們或有不同的參數個數或有不同的參數類型,則稱為方法的重載(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)。這就是那句話的意義所在。
個人讀后的一點思考:
第5個問題值得思考,輸出 a2.show(c) ,答案是 B and A
分析:
a2是類A的引用變量,引用的是類B的一個實例對象。c是類C的一個實例。
1)this.show(c),在A中找show(C obj)的方法,沒有找到,進行第二步。
2)super.show(c),A沒有超類,進行第三步。
3)this.show(super(c)), super(c)是B,A中是沒有show(B obj)方法的,但是這時候會繼續往上找,找B的父類,super(B)是A,在A中找show(A obj), 找到了。
4)a2引用的是類B的一個對象,由B來決定調用哪個方法,因為類B中覆蓋了A的show(A obj)方法,因此最終用類B中的show(A obj)方法。
所以結論是這里super(O)是采用了逐層往上尋找的方式,具體來說,父類找不到對應的show方法,就找父類的父類,如果還沒有,繼續往上找,直到找到根基類。
為了驗證作者原文中藍色字體段文字的正確性,我測試了輸出a1.show(c),結果和預想的一樣,是A and A.
這里,a1.show(c)和a2.show(c)的區別在于持有的引用對象的不同,a1是持有了A類的一個實例對象,而a2是持有了B類的一個實例對象,所以當都找到了A類中的show(A obj)時,引用的對象的類型決定了該調用哪個成員的方法,前提是這個被調用的方法必須被子類覆蓋了。所以a1.show(c)輸出的是A and A,而 a2.show(c)輸出的是 B and A.