注意: 裝載 和 加載 的區別:java
jvm把class文件加載到內存中,並對數據進行校驗、解析和初始化,最終造成JVM能夠直接使用的java類的全過程。安全
class只有使用的時候纔會被裝載,java虛擬機也不會無條件的裝載class類型數據結構
加載類處於類裝載的第一個階段,將class文件的字節碼加載到內存中,並將靜態數據轉換成方法區中的運行時數據結構,在堆中生成一個表明這個類的java.lang.Class對象,做爲方法區類數據的訪問入口。jvm
該過程須要ClassLoader參與。佈局
加載類,JVM必須完成:測試
將java類的二進制代碼合併到JVM的運行狀態中。這一步包含三個操做:優化
注意:線程
java並不支持boolean類型,對於boolean類型,內部實現是Int,因爲int的默認值是0,故對應的boolean默認值是false。code
此處進行內存分配的只是類變量(static修飾的變量),不包括實例變量(實例變量會在對象實例化時隨着對象一塊兒分配在java堆中)對象
解析,該階段的任務就是將類、接口、字段和方法的符號引用轉爲直接引用
符號應用,就是一些字面量的引用,和虛擬機的內部數據結構和內存佈局無關。
初始化是類裝載的最後一個階段。若是前面的操做沒有問題,表示類能夠順利裝載到系統中。此時,類纔會開始執行java字節碼。
該階段的重要工做,是執行類的初始化方法<clinit>, 爲類變量賦予正確的值。方法<clinit>是由編譯器自動生成的,它是由類靜態成員的賦值語句以及static語句塊合併產生的。
在加載一個類以前,虛擬機老是會試圖加載該類的父類,所以父類的<clinit>方法老是在子類<clinit>以前被調用。
注意:
JVM規定:一個類或者接口在初次使用時,必須進行初始化。這裏的「使用」,指的是」主動使用」,包括如下幾種狀況:
除了以上狀況屬於主動使用外,其餘均屬於被動使用,被動使用不會引發類的初始化。
public class Parent { static { System.out.println("Parent init."); } } public class Child extends Parent { static { System.out.println("Child init."); } } public class InitMain { public static void main(String[] args) { Child child = new Child(); } }
上述示例聲明瞭三個類:Parent、Child(extends Parent)、InitMain。若Parent被初始化,static被執行將打印「Parent Init」;若Child被初始化,將會打印「Parent init」、"Child init"。執行InitMain,打印結果爲:
Parent init. Child init.
根據上述示例可知,系統首先加載Parent類,接着裝載Child類。符合主動裝載中的兩個條件,使用new關鍵字建立類的實例會裝載相關的類,以及在初始化子類時,必選先初始化父類。
public class Parent { static { System.out.println("Parent init"); } public static int v = 100; } public class Child extends Parent { static { System.out.println("Child init."); } } public class InitMain { public static void main(String[] args) { System.out.println(Child.v); } }
說明:Parent類中定義了類變量v,在InitMain測試類中,使用子類Child調用父類中的類變量v。
執行結果:
Parent init 100
在InitMain測試類中,經過子類Child直接訪問了Parent類中的static變量v,可是子類Child並未初始化,只有父類Parent完成初始化。因此,在引用一個字段時,只有直接定義該字段的類,纔會被初始化。
注意:雖然子類Child沒有被初始化,可是此時Child類已經被系統加載,只是沒有進入到初始化階段。
public class FinalFieldClass { public static final String CONST_STR = "hello world"; static { System.out.println("FinalFieldClass init"); } } public class FinalFieldTest { public static void main(String[] args) { System.out.println(FinalFieldClass.CONST_STR); } }
運行結果: hello world.
分析:FinalFiledClass類沒有由於其常量字段CONST_STR被引用而初始化,這是由於在Class文件生成時,final常量因爲其不變性,作了適當的優化。
編譯後的FinalFieldClass.class中,並無引用FinalFieldClass類,而是將其final常量直接存放在常量池中,所以FinalFiledClass類天然不會被加載。javac在編譯時,將常量直接植入目標類,再也不使用被引用類。
注意:並非在代碼中出現的類,就必定會被加載或者初始化,若是不符合主動使用的條件,類就不會被初始化。