1. 程式人生 > >Java類與對象初始化的過程(一道經典的面試題)

Java類與對象初始化的過程(一道經典的面試題)

java語法 ade 還要 body image LV 賦值 準備 new t

本文不再以ClassLoader的視角解釋這些問題。

首先,Java代碼有個特點,就是成員變量可以在前面的方法中使用,在後面定義。這一特性,很多人說Java了不起,可是為什麽呢?Java為何能夠這樣呢?

我們首先來看一道面試題:

技術分享圖片

寫出上面代碼的運行結果。 其實對於Java了解比較深入的人,不屑於解決這道題,因為看代碼寫運行結果,再常規不過,可是這個題,要是寫準了,還真的不容易,因為,我們要以類似C語言的視角審視這道題。 OK,我們首先理解一個基本概念,內存空間一定是先申請,再初始化,再使用的。例如C語言中的malloc分配空間,然後初始化寫0,然後使用。其實Java也是如此的。 首先,我們從main方法入手,只有一行new對象(第26行)。 new對象首先要加載類,這是肯定的了,因此“Test”需要加載類,也就是加載Test的字節碼文件到方法區(永久代)中。這時需要把類中的“static”部分處理完成。 也就是說,static部分是隨著Test的字節碼文件進入到永久代的。 它的過程是:先把所有的static部分申請空間,然後再給每一個static成員由上至下分配初始值。初始值有兩種,一種是Java對於基本數據類型的默認初始值,這個默認初始值在申請空間之時就給每一個成員賦予了,另一種是Java程序員利用“=”對成員進行賦初始值。 技術分享圖片
如圖所示,五個靜態成員在永久代首先被分配內存。 此時,k=0,t1=null,t2=null,i=0,n=0。空間申請完成之後,我們把0賦值給k,雖然k被分配內存之後就是0,但是依然還要把0再賦值給k,因為“=”右邊的“0”是程序員對k的賦值。然後是給t1實例一個對象,也就是t1原本是null,現在把new出來的對象賦值給t1,於是就要構建一個Test對象。構建對象時,要把所有的非static部分初始化一份,放入堆內存。 這時,你就理解了Java語法中,為什麽靜態成員是“類名.成員”,而非靜態成員是“對象.成員”了。因為所屬關系不同。 那麽我們開始找非靜態的成員 技術分享圖片 如圖,有j和一個構造塊。 因此是先給j申請空間,然後運行print("j")方法,把方法的返回值交給j。 於是,這個程序的第一段打印結果出來了: 技術分享圖片
打印: 技術分享圖片 此後,k=1 , n=1 , i=1。 然後是接著找非靜態的部分,就只有構造塊了,因此是: 技術分享圖片 此後,k=2 , n=2 , i=2。 這時,我們構造對象的準備工作做完了,也就是非靜態的代碼都執行完了,因此開始實例化對象,Java實例化對象使用構造方法,因此執行: 技術分享圖片 打印: 技術分享圖片 此後,t1不再是null,然後初始化t2,過程和t1一樣,因此運行結果是: 技術分享圖片 再然後初始化靜態的i,因此是執行: 技術分享圖片 輸出打印: 技術分享圖片 然後初始化 n(第6行),直接把n賦值為99。但是什麽都不打印。 然後再往下是靜態塊: 技術分享圖片 輸出打印: 技術分享圖片 至此,所有的靜態部分也都初始化完畢了,可以new Test("init")了: 技術分享圖片
輸出打印: 技術分享圖片 所以總體打印如下: 技術分享圖片 總結,其實Java本身也是代碼從上往下走的,只不過靜態部分和非靜態部分在兩個次元裏。Java的成員有分配空間,賦默認值兩個過程,且首先為全體成員申請空間,然後由上至下逐一賦值。

Java類與對象初始化的過程(一道經典的面試題)