整體概覽
類加載
- 在Java代碼中谢揪,類型的加載、連接阎姥、初始化過程都是在程序運行期間完成的
- 提供了更大的靈活性记舆,增加了更多的可能性
類的加載、連接與初始化
加載:查找并加載類的二進制數(shù)據(jù)
-
連接
- 驗證:確保被加載類的正確性
- 準備:為類的靜態(tài)變量分配內(nèi)存呼巴,并將其初始化為默認值
- 解析:把類中的符號引用轉(zhuǎn)換為直接引用
初始化:為類的靜態(tài)變量賦予正確的初始值
類的加載
- 類的加載指的是將類的.class文件中的二進制數(shù)據(jù)讀入到內(nèi)存中泽腮,將其放在運行時數(shù)據(jù)區(qū)的方法區(qū)內(nèi),然后在內(nèi)存中創(chuàng)建一個java.lang.Class對象(規(guī)范并未說明Class對象位于哪里衣赶,HotSpot虛擬機將其放在了方法區(qū)中)用來封裝類的方法區(qū)內(nèi)的數(shù)據(jù)
- 加載class文件的方式
- 從本地系統(tǒng)中直接加載
- 通過網(wǎng)絡下載.class文件
- 從zip诊赊,jar等歸檔文件中加載.class文件
- 從專有數(shù)據(jù)庫中提取出.class文件
- 將Java源文件動態(tài)編譯為.class文件
- 類的加載的最終產(chǎn)品是位于內(nèi)存中的Class對象
- 類加載器不需要等到某個類被“首次主動使用”時再加載它,JVM規(guī)范允許類加載器在預料某個類將要被使用時預先加載它府瞄,如果與先加載過程中遇到了.class文件缺失或存在錯誤碧磅,類加載器必須在程序首次主動使用該類時才報告錯誤
- 如果這個類一直沒被程序主動使用,那類加載器就不會報告錯誤
類的連接
- 將已經(jīng)讀入到內(nèi)存的類的二進制數(shù)據(jù)合并到虛擬機的運行時環(huán)境中去
- 將類與類之間的關系確定好遵馆,并且對字節(jié)碼相關的處理鲸郊、驗證、校驗等操作通過加載連接完成
驗證
- 類文件的結(jié)構檢查
- 語義檢查
- 字節(jié)碼驗證
- 二進制兼容性的驗證
準備
- 準備階段货邓,Java虛擬機為類的靜態(tài)變量分配內(nèi)存秆撮,并設置默認的初始值。例如以下示例逻恐,在準備階段將為int類型的靜態(tài)變量a分配4個字節(jié)的內(nèi)存空間像吻,并且賦予默認值0,為long類型的靜態(tài)變量b分配8個字節(jié)的內(nèi)存空間复隆,并且賦予默認值0.
public class Sample{
private static int a = 1;
public static long b;
static {
b = 2;
}
}
類的初始化
初始化途徑
- 在初始化階段拨匆,Java虛擬機執(zhí)行類的初始化語句,為類的靜態(tài)變量賦予初始值挽拂。在程序中惭每,靜態(tài)變量的初始化有兩種途徑:
- 在靜態(tài)變量的聲明處進行初始化
- 在靜態(tài)代碼塊中進行初始化
- Java虛擬機按照初始化語句的類文件的先后順序依次執(zhí)行它們
例如以下代碼,靜態(tài)變量a和b都被顯式初始化亏栈,而靜態(tài)變量c沒有被顯式初始化台腥,它將保持默認值0,按照先后順序a最終將取值為4
public class Sample{
private static int a = 1;
public static long b;
public static long c;
static {
b = 2;
}
static {
a = 4;
}
}
初始化時機
所有的Java虛擬機實現(xiàn)必須在每個類或接口被Java程序“首次主動使用”時才初始化他們
-
主動使用(七種)
- 創(chuàng)建類的實例
- 訪問某個類或接口的靜態(tài)變量绒北,或者對該靜態(tài)變量賦值
- 調(diào)用類的靜態(tài)方法
- 反射(如Class.forName("com.test.Test"))
- 初始化一個類的子類
- Java虛擬機啟動時被表明為啟動類的類(Java Test)
- JDK1.7開始提供的動態(tài)語言支持:java.lang.invoke.MethodHandle實例的解析結(jié)果REF_getStatic,REF_putStatic,REF_invokeStatic句柄對應的類沒有初始化黎侈,則初始化
除了以上七中情況,其他使用Java類的方式都被看作是對類的被動使用闷游,都不會導致類的初始化
參考資料:
圣思園JVM課程