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

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

首先,Java代碼有個特色,就是成員變量能夠在前面的方法中使用,在後面定義。這一特性,不少人說Java了不得,但是爲何呢?Java爲什麼可以這樣呢?面試

咱們首先來看一道面試題: 3d

寫出上面代碼的運行結果。
其實對於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的成員有分配空間,賦默認值兩個過程,且首先爲全體成員申請空間,而後由上至下逐一賦值。
相關文章
相關標籤/搜索