Java多態性探悉

Java多態性探悉

 

一、基本概念

 

     多態性:發送消息給某個對象,讓該對象自行決定響應何種行爲。

     通過將子類對象引用賦值給超類對象引用變量來實現動態方法調用。

 

     java 的這種機制遵循一個原則:當超類對象引用變量引用子類對象時,被引用對象的類型而不是引用變量的類型決定了調用誰的成員方法,但是這個被調用的方法必須是在超類中定義過的,也就是說被子類覆蓋的方法。

 

     1. 如果a是類A的一個引用,那麼,a可以指向類A的一個實例,或者說指向類A的一個子類。

     2. 如果a是接口A的一個引用,那麼,a必須指向實現了接口A的一個類的實例。

 

 

二、Java多態性實現機制

 

     SUN目前的JVM實現機制,類實例的引用就是指向一個句柄(handle)的指針,這個句柄是一對指針:

     一個指針指向一張表格,實際上這個表格也有兩個指針(一個指針指向一個包含了對象的方法表,另外一個指向類對象,表明該對象所屬的類型);

     另一個指針指向一塊從java堆中爲分配出來內存空間。

 

     The Java Virtual Machine does not require any particular internalstructure for objects. In Sun's current implementation of the Java VirtualMachine, a reference to a class instance is a pointer to a handle that isitself a pair of pointers: one to a table containing the methods of the objectand a pointer to the Class object that represents the type of the object, andthe other to the memory allocated from the Java heap for the object data. (jvm 規範中關於對象內存佈局的說明)

 

 

三、總結

 

     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已經使用了編譯器產生的類型信息調整轉換了。

        這裏你可以這樣理解,相當於把不是父類中含有的函數從虛擬函數表中設置爲不可見的。注意有可能虛擬函數表中有些函數地址由於在子類中已經被改寫了,所以對象虛擬函數表中虛擬函數項目地址已經被設置爲子類中完成的方法體的地址了。

 

 

     4、Java與C++多態性的比較

 

     jvm關於多態性支持解決方法是和c++中幾乎一樣的,

     只是c++中編譯器很多是把類型信息和虛擬函數信息都放在一個虛擬函數表中,但是利用某種技術來區別。

 

     Java把類型信息和函數信息分開放。Java中在繼承以後,子類會重新設置自己的虛擬函數表,這個虛擬函數表中的項目有由兩部分組成。從父類繼承的虛擬函數和子類自己的虛擬函數。

     虛擬函數調用是經過虛擬函數表間接調用的,所以才得以實現多態的。

 

     Java的所有函數,除了被聲明爲final的,都是用後期綁定。

     C++實現多態性,使用關鍵字virtual,爲了引起晚捆綁,使用虛函數。若一個函數在基類被聲明爲virtual,則所有子類中都是virtual的。對虛函數的重定義成爲越位。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章