JAVA多態性我和網友交流實錄

我在這裏公開和我dennisboys的學習交流信件,我們主要在談java中多態性問題。
這裏感謝我的好朋友能給我一個結合自己能力解釋問題的機會,在解釋中有出錯
或者需要討論的部分希望能通知我一聲。


上篇關於多態性的文章請參閱我前面寫的<<java多態性實例解釋一文>>.



*******************************************************************************
    網友dennisboys的提問部分開始
*******************************************************************************
從頭到尾把你的代碼看了n次,畫了n個圖(關於父類和子類的函數地址圖),有以下一些迷惑
test t=new test();
base b=new base();
b=t;


/*
問題一:
這裏是把父類的引用指向子類,那是不是說調用父類的方法就等於調用子類的方法呢?就是
如果b.display2()不是調用子類的display2()方法嗎?而子類有display2()方法,爲什麼不能
調用呢?(我試過寫代碼了,果然如你所說是出錯的。)


對於以上的問題,你有一段文字好像是對其作解譯的。不過我不太清楚。不知是不是以下這
段。


引用原文:
同時可能有人要問,子類中那個函數地址不是也在虛擬函數表中嗎?
很高興你問這樣的問題,但是父類引用看到的虛擬函數表是沒有
那個函數項目的,因爲他對於父類引用絕對是不可見。


我現在理解是如果在子類中如對方法作了更改或新增的方法,對父類來說是不可見的?理解
對嗎??
*/


問題二:
((test)b).display();
另一個迷感就是你說的強制轉換類型的語法到底是怎樣的??我不明白爲((test)b)代表什麼


不過說回來(我怎麼覺得這個例子是在說繼承??),這個例子使我對繼承有了很深的認識
,我倒是覺得對多態性還是一知半解,(別說我笨笨)可能我還沒清楚到底哪用到了多態性,
不過請先回復了我以上兩個問題,希望你回覆了我以上兩個問題(結合我研究你的代碼)使我
對多態性有更好更深的認識。。
*******************************************************************************
    網友dennisboys的提問部分結束
*******************************************************************************



*******************************************************************************
    zosatapo的解釋部分開始
*******************************************************************************


你真的應該謝謝我的,因爲我辛辛苦苦打了篇文章,
因爲網絡原因沒有能成功保存,害了我重寫一次,
沒有辦法,誰叫你是我的好朋友呢。


首先謝謝你耐心看完我的文章。


下面我就我上次寫的那篇文章和你的問題作簡單的說明但是又比較複雜的說明。


我上次寫的那篇文章存在一處寫作錯誤,我在網上已經修正了,這裏告訴你一下:


上次的原文中有這樣的幾句話
****************************************
  // 下面調用會出錯的
  file://t.display2();
  file://應該象下面那樣
  ((test)b).display2();
******************************************


這幾句話寫作上有錯誤:
應該改成成下面這樣:


**********************************************
  // 下面調用[不]會出錯的  (這裏多了一個[不]字)
  file://t.display2(); 
#########################################  
  實際上我上面這兩行跟本文沒有關係的
  但是爲了你很好的理解動態性,你可以
  把上面的代碼與下面的進行比較。
#########################################
  file://下面這行調用不會出錯
  file://b.display2();  這裏加了一行
  file://應該象下面那樣
  ((test)b).display2();
**********************************************‘



下面正式開始我們今天的話題,正對你的問題我進行解釋,
這裏我儘可能的解釋詳細一點讓你明白,實際上這裏太複雜
涉及到OOP的具體實現問題,這個問題又不得不涉及到編譯器
問題,主要的又是對象內存佈局問題。


由於第二個問題比較簡單,我把回答問題的次序顛倒一下。


*********
  問題二
*********
你說的很對,我這裏解的是繼承,但是我這裏解的不僅僅是繼承的
問題。可以這樣是你說的繼承只是我解釋動態性問題的一個途徑和
手段而已,因爲繼承和多態性一樣是面向對象中很重要的概念,不
是寫一點文章就可以說明白的。


簡單一點和不精確的說,動態性與是繼承不可分割的,如果沒有繼承
根本就談不上多態性的。所以我說你說的對,但是你沒有真正明白我
例子的作用(55555555~~~~~~~~~~~~~我的心血呀)


*********
  問題一
*********
首先需要說明的是你對這個問題的理解是不正確的。


這個問題更是複雜的一塌糊塗,我儘量用一些不標準的詞彙來說明這個問題。
因爲這樣便於理解。我這裏不解太多的理論,因爲我自己現在也正在研究jvm
規範的,因爲很多東西java和c++不同,雖然兩者實現很相似。上面這點,
我是根據我個人的一些實踐,包括理論方面和程序編寫實踐得到的。但是
我也不敢全部拿c++的那套實現講給你聽,實際上講了你也不一定聽得懂的。
我這裏主要講一些基本的知識,你記住就可以了的,等你學習深入的時候
我再給解釋,也許那時候你自然就懂了。
實際上在繼承以後,子類會重新設置自己的虛擬函數表,
這個虛擬函數表中的項目有由兩部分組成。從父類繼承的虛擬函數和子類自己
的虛擬函數。


記住一個很簡單又很複雜的規則,一個類型引用只能引用引用類型自身含有的
方法和變量。你可能說這個規則不對的,因爲父類引用指向子類對象的時候,
引用是子類的方法的。我告訴你這個規則對於這樣的情況依然是成立的。放鬆
你的大腦,不要想一些亂七八糟的事情,仔細聽我分析。


對了,到這裏的時候我假設你對於上面的規則除了虛擬函數調用的情況下,
其他的靜態函數引用,以及變量引用都明白了。


下面我們開始我們的重量級說明。虛擬函數引用。
下面是jvm規範中關於對象內存佈局的說明,我沒有翻譯
我想你可以看明白,反正我現在看英文沒有問題,
如果不明白就查字典。
The Java Virtual Machine does not require any particular internal
structure for objects. In Sun's current implementation of the Java
Virtual Machine, a reference to a class instance is a pointer
to a handle that is itself a pair of pointers: one to a table
containing the methods of the object and a pointer to the
Class object that represents the type of the object, and
the other to the memory allocated from the Java heap for
the object data.


根據這裏我就知道實際上jvm關於多態性支持解決方法是和c++中幾乎一樣的,
只是c++中編譯器很多是把類型信息和虛擬函數信息都放在一個虛擬函數表中,
但是利用某種技術來區別。


所以當你使用父類引用指向子類的時候,其實jvm已經使用了編譯器產生的類型
信息調整轉換了。這裏你可以這樣理解,相當於把不是父類中含有的函數從虛擬
函數表中設置爲不可見的。注意有可能虛擬函數表中有些函數地址由於在子類中
已經被改寫了,所以對象虛擬函數表中虛擬函數項目地址已經被設置爲子類中完成
的方法體的地址了。


上面這一段就是爲什麼父類引用指向子類對象時候,有的方法可以調用,有的方法
卻不能調用。


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

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