前言
學過Java
的人都知道,Object
是所有類的父類。但是你有沒有這樣的疑問,我並沒有寫extends Object
,它是怎麼預設繼承Object的呢?
那麼今天我們就來看看像Java這種依賴於虛擬機器的程式語言是怎樣實現預設繼承Object的,以及Java編譯器
和JVM
到底是如何做的?
繼承自Object驗證
首先我們來驗證一下Object是不是所有類的父類,隨便新建一個Java類,如下圖:
從上面的程式碼可以看出,new MyClass()打點之後可以選擇呼叫的方法有很多,我們定義的MyClass類裡面只有一個main方法,那這些方法哪來的,顯然是Object裡宣告的,故MyClass類的父類就是Object,因此,在MyClass中可以使用Object類的public或protected資源。另外,當A類繼承MyClass類時,通過打點也可以調到Object內的方法,這是繼承的傳遞,好比Object是MyClass的“父親”,MyClass是A類的“父親”,Object是A類的“爺爺”,間接的繼承了Object。
因此,Object是超類,是所有類的父類。
推測可能的原因
要了解Java類是如何預設繼承Object的?
的原因其實並不需要知道JVM的實現細節。只需瞭解一下對於這種虛擬機器程式的基本原理即可。一般對於這種靠虛擬機器執行的語言(如Java、C#等)會有兩種方法處理預設繼承問題。
編譯器處理
在編譯原始碼時,當一個類沒有顯式標明繼承的父類時,編譯器會為其指定一個預設的父類(一般為Object),而交給虛擬機器處理這個類時,由於這個類已經有一個預設的父類了,因此,VM仍然會按照常規的方法像處理其他類一樣來處理這個類。對於這種情況,從編譯後的二進位制角度來看,所有的類都會有一個父類(後面可以以此依據來驗證)。
JVM處理
編譯器仍然按照實際程式碼進行編譯,並不會做額外的處理,即如果一個類沒有顯式地繼承於其他類時,編譯後的程式碼仍然沒有父類。然後由虛擬機器執行二進位制程式碼時,當遇到沒有父類的類時,就會自動將這個類看成是Object類的子類(一般這類語言的預設父類都是Object)。
驗證結論
從上面兩種情況可以看出,第1種情況是在編譯器上做的文章,也就是說,當沒有父類時,由編譯器在編譯時自動為其指定一個父類。第2種情況是在虛擬機器上做文章,也就是這個預設的父類是由虛擬機器來新增的。
那麼Java是屬於哪一種情況呢?其實這個答案很好得出。只需要隨便找一個反編譯工具,將.class檔案進行反編譯即可得知編譯器是如何編譯的。
就以上面程式碼為例,如果是第1種情況,就算MyClass沒有父類,但由於編譯器已經為MyClass自動新增了一個Object父類,所以,在反編譯後得到的原始碼中的MyClass類將會繼承Object類的。如果不是這種情況,那麼就是第2種情況。
那麼實際情況是什麼樣的呢?現在我們就將MyClass.class反編譯看看到底如何。
jd-gui反編:
使用JDK自帶的工具(javap)反編譯CMD命令列下執行:javap MyClass>MyClass.txt
extends Object
,使用排除法,因此是第2情況。
這樣來推匯出的結論是第2種情況,但事實真的如此嗎?為什麼網上還有說反編譯後的是有extends Object
字樣?
JDK版本問題?
猜想是JDK版本的問題,於是把JDK版本切換到7,使用jd-gui和javap反編譯,接果和使用JDK8反編譯後的結果一樣,也都沒有extends Object
。
繼續換版本,昨晚在宿舍準備到Oracle官網下載JDK 6,但是死活下不來,今早到公司後第一件事就是下載,很順利,安裝後把JDK版本切換到JDK 6。
仍然在CMD視窗執行javap MyClass>MyClass.txt
,得到的TXT檔案內容如下:
extends Object
,jd-gui反編譯後的依然沒有。
即,JDK 6之前使用javap反編譯後的MyClass類顯式的繼承Object,JDK 7以後沒有;jd-gui反編譯後的不管JDK版本如何始終沒有。我們以java自帶的工具為準。
總結
那麼就是說JDK 6之前是編譯器
處理,JDK 7之後是虛擬機器
處理。
但是仔細想想我們在編輯器
裡(IDEA)打點時就能列出Object類下的方法,此時還沒輪到編譯器和jvm,編輯器就已經知道MyClass類的父類是Object類了,這是因為編輯器為我們做了一些智慧處理。
【end】
參考文獻:
java中 建立一個新的類 怎麼預設繼承Object類的: zhidao.baidu.com/question/12…