一、基本概念
多態性:發送訊息給某個對象,讓該對象自行決定響應何種行為。
通過將子類對象引用賦值給超類對象引用變數來實現動態方法引動過程。
java 的這種機制遵循一個原則:當超類對象引用變數引用子類對象時,被引用對象的類型而不是引用變數的類型決定了調用誰的成員方法,但是這個被調用的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。
1.如果a是類A的一個引用,那麼,a可以指向類A的一個執行個體,或者說指向類A的一個子類。
2.如果a是介面A的一個引用,那麼,a必須指向實現了介面A的一個類的執行個體。
二、Java多態性實現機制
SUN目前的JVM實現機制,類執行個體的引用就是指向一個控制代碼(handle)的指標,這個控制代碼是一對指標:
一個指標指向一張表格,實際上這個表格也有兩個指標(一個指標指向一個包含了對象的方法表,另外一個指向類對象,表明該對象所屬的類型);
另一個指標指向一塊從java堆中為分配出來記憶體空間。
三、總結
1、通過將子類對象引用賦值給超類對象引用變數來實現動態方法引動過程。
DerivedC c2=new DerivedC();
BaseClass a1= c2; //BaseClass 基類,DerivedC是繼承自BaseClass的子類
a1.play(); //play()在BaseClass,DerivedC中均有定義,即子類覆寫了該方法
分析:
* 為什麼子類的類型的對象執行個體可以覆給超類引用?
自動實現向上轉型。通過該語句,編譯器自動將子類執行個體向上移動,成為通用類型BaseClass;
* a.play()將執行子類還是父類定義的方法?
子類的。在運行時期,將根據a這個對象引用實際的類型來擷取對應的方法。所以才有多態性。一個基類的對象引用,被賦予不同的子類對象引用,執行該方法時,將表現出不同的行為。
在a1=c2的時候,仍然是存在兩個控制代碼,a1和c2,但是a1和c2擁有同一塊資料記憶體塊和不同的函數表。
2、不能把父類對象引用賦給子類對象引用變數
BaseClass a2=new BaseClass();
DerivedC c1=a2;//出錯
在java裡面,向上轉型是自動進行的,但是向下轉型卻不是,需要我們自己定義強制進行。
c1=(DerivedC)a2; 進行強制轉化,也就是向下轉型.
3、記住一個很簡單又很複雜的規則,一個類型引用只能引用參考型別自身含有的方法和變數。
你可能說這個規則不對的,因為父類引用指向子類對象的時候,最後執行的是子類的方法的。
其實這並不矛盾,那是因為採用了後期綁定,動態啟動並執行時候又根據型別去調用了子類的方法。而假若子類的這個方法在父類中並沒有定義,則會出錯。
例如,DerivedC類在繼承BaseClass中定義的函數外,還增加了幾個函數(例如 myFun())
分析:
當你使用父類引用指向子類的時候,其實jvm已經使用了編譯器產生的類型資訊調整轉換了。
這裡你可以這樣理解,相當於把不是父類中含有的函數從虛擬函數表中設定為不可見的。注意有可能虛擬函數表中有些函數地址由於在子類中已經被改寫了,所以對象虛擬函數表中虛擬函數項目地址已經被設定為子類中完成的方法體的地址了。