圖解JAVA對象的創建過程

前面幾篇博文分別介紹了JAVA的Class文件格式、JVM的類加載機制和JVM的內存模型,這裏就索性把java對象的創建過程一併說完,這樣java對象的整個創建過程就基本上說明白了(當然你要有基礎才能真正看明白)。經常有人問我爲什麼這麼喜歡鑽研底層的東西,首先,因爲我以前的做硬件的和嵌入式的,興趣使然;其次,我個人感覺,如果不把上下打通,心裏老是有一堵牆過不去,說白了,這是個人因素,與好壞無關(當然,經常有人說,懂底層原理是成爲高手的必經之路)。

現在來說一下我當初學習JVM的原因,在學習JAVA之前,我只學過C/C++,當我接觸JAVA之後,發現JAVA與C++之間有着巨大的差異,其中最困惑我的就是C++的繼承和JAVA的繼承實現原理是否一樣(對象如何最終定位到字段,無論這個字段是自身的還是繼承過來的)?多態的實現機制是否相同?等等。因爲之前看過《深度探索C++對象模型》,對C++的對象模型略知一二(一直打算寫一個C++對象模型的文章,苦於沒時間,後面一定補上),所以我感覺JVM可以解答我的疑惑。

關於對象的創建過程一般是從new指令(我說的是JVM的層面)開始的(具體請看圖1),JVM首先對符號引用進行解析,如果找不到對應的符號引用,那麼這個類還沒有被加載,因此JVM便會進行類加載過程(具體加載過程可參見我的另一篇博文)。符號引用解析完畢之後,JVM會爲對象在堆中分配內存,HotSpot虛擬機實現的JAVA對象包括三個部分:對象頭、實例字段和對齊填充字段(具體內容請看圖2),其中要注意的是,實例字段包括自身定義的和從父類繼承下來的(即使父類的實例字段被子類覆蓋或者被private修飾,都照樣爲其分配內存)。相信很多人在剛接觸面嚮對象語言時,總把繼承看成簡單的“複製”,這其實是完全錯誤的。JAVA中的繼承僅僅是類之間的一種邏輯關係(具體如何保存記錄這種邏輯關係,則設計到Class文件格式的知識,具體請看我的另一篇博文),唯有創建對象時的實例字段,可以簡單的看成“複製”。

爲對象分配完堆內存之後,JVM會將該內存(除了對象頭區域)進行零值初始化,這也就解釋了爲什麼JAVA的屬性字段無需顯示初始化就可以被使用,而方法的局部變量卻必須要顯示初始化後纔可以訪問。最後,JVM會調用對象的構造函數,當然,調用順序會一直上溯到Object類。

至此,一個對象就被創建完畢,此時,一般會有一個引用指向這個對象。在JAVA中,存在兩種數據類型,一種就是諸如int、double等基本類型,另一種就是引用類型,比如類、接口、內部類、枚舉類、數組類型的引用等。引用的實現方式一般有兩種,具體請看圖3。此處說一句題外話,經常用人拿C++中的引用和JAVA的引用作對比,其實他們兩個只是“名稱”一樣,本質並沒什麼關係,C++中的引用只是給現存變量起了一個別名(引用變量只是一個符號引用而已,編譯器並不會給引用分配新的內存),而JAVA中的引用變量卻是真真正正的變量,具有自己的內存空間,只是不同的引用變量可以“指向”同一個對象而已。因此,如果要拿C++和JAVA引用對象的方式相對比,C++中的指針倒和JAVA中的引用如出一轍,畢竟,JAVA中的引用其實就是對指針的封裝。

注:本文爲原創博文,轉載請註明出處。

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