標籤:static成員 idt 概念 進入 分享圖片 gpo init post 準備
本文不再以ClassLoader的視角解釋這些問題。
首先,Java代碼有個特點,就是成員變數可以在前面的方法中使用,在後面定義。這一特性,很多人說Java了不起,可是為什麼呢?Java為何能夠這樣呢?
我們首先來看一道面試題:
寫出上面代碼的運行結果。其實對於Java瞭解比較深入的人,不屑於解決這道題,因為看代碼寫運行結果,再常規不過,可是這個題,要是寫准了,還真的不容易,因為,我們要以類似C語言的視角審視這道題。OK,我們首先理解一個基本概念,記憶體空間一定是先申請,再初始化,再使用的。例如C語言中的malloc分配空間,然後初始化寫0,然後使用。其實Java也是如此的。首先,我們從main方法入手,只有一行new對象(第26行)。new對象首先要載入類,這是肯定的了,因此“Test”需要載入類,也就是載入Test的位元組碼檔案到方法區(永久代)中。這時需要把類中的“static”部分處理完成。也就是說,static部分是隨著Test的位元組碼檔案進入到永久代的。它的過程是:先把所有的static部分申請空間,然後再給每一個static成員由上至下分配初始值。初始值有兩種,一種是Java對於基礎資料型別 (Elementary Data Type)的預設初始值,這個預設初始值在申請空間之時就給每一個成員賦予了,另一種是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類與對象初始化的過程(一道經典的面試題)