版權聲明:本文爲博主原創文章,轉載請註明出處,歡迎交流學習!java
你們都知道Java程序被編譯器編譯成字節碼文件保存在硬盤裏,Java虛擬機在執行代碼時首先要把編譯後的字節碼文件從硬盤加載到內存中,而後才能執行。這裏所說的加載包括一連串的動做,即虛擬機把字節碼文件加載到內存,並對數據進行校驗、轉換解析和初始化,最終造成能夠被虛擬機直接使用的Class對象,這就是虛擬機的類加載機制。數據結構
類從被加載到虛擬機內存中開始,到卸載出內存爲止,它的整個生命週期包括:加載、驗證、準備、解析、初始化、使用和卸載7個階段。前面5個階段是類加載的過程,初始化階段是類加載過程的最後一步,在初始化階段,會爲類的靜態變量賦予初始值(聲明靜態變量時顯式賦值,或者在static塊中賦值),要注意的是,在準備階段,虛擬機已經爲類的靜態變量初始化默認值,而在初始化階段的賦值是咱們程序主觀給定的初始值,二者並不衝突。到了初始化階段,才真正開始執行類中定義的程序代碼。學習
相信不少程序猿都有過這樣一個疑問,什麼狀況下須要開始類加載過程當中的第一階段:加載?(這裏加載指的是將類的.class文件中的二進制數據讀入到內存中,將其放入到運行時數據區的方法區內,而後在堆區建立一個java.lang.Class對象,用來封裝類在方法區內的數據結構)我一樣也被這個問題困擾過,Java虛擬機規範中對什麼時候開始加載並無進行強制約束,交給虛擬機的具體實現來自由把握。Java語言在運行期間是動態加載的,在第一次須要使用類信息時會進行加載。好比:調用靜態成員變量或方法、實例化一個對象、加載子類時會先加載父類。spa
可是對於初始化階段,虛擬機規範則嚴格的規定了有且只有在對類首次主動使用時,才執行初始化操做(加載、驗證、準備階段在此以前進行)。對象
Java程序對類的使用方式分爲兩種:主動使用和被動使用。全部的Java虛擬機實現必須在每一個類或接口被Java程序「首次主動使用」時才初始化這個類,不然不執行初始化階段操做。
什麼是對類的主動使用和被動使用?在Java程序中,一共有6種狀況會被稱爲對類的主動使用,除了這6種,其餘全部的對類的使用方式都被稱爲被動使用。當這6種方式出現時若是類沒有進行過初始化,則會觸發其進行初始化。接口
主動使用(6種):生命週期
一、建立類的實例內存
二、訪問某個類或接口的靜態變量,或者對該靜態變量賦值編譯器
三、調用某個類的靜態方法虛擬機
四、反射(Class.forName("java.lang.String"))
五、初始化一個類的子類
六、Java虛擬機啓動時被標明爲啓動類的類
前3種狀況很好理解,第4種運用反射返回一個Class對象,這時與Class對象相關聯的類會被初始化。第5種初始化一個類的子類是指,給子類的靜態變量賦予初始值,這時父類會先進行初始化。第6種當一個類被標明爲啓動類,例如一個類中含有main方法,這個類就是啓動類,Java虛擬機啓動時會加載並初始化這個類。
這裏再強調一遍,除了以上6種狀況,其餘全部對類的操做都不會觸發虛擬機對類進行初始化操做,即在類加載過程當中不會執行初始化這一階段。