Java程序主動(dòng)使用某個(gè)類時(shí),如果該類還沒有被加載到內(nèi)存中,則系統(tǒng)會(huì)通過加載、連接、初始化三個(gè)步驟來對(duì)該類進(jìn)行初始化。JVM會(huì)連續(xù)完成這三個(gè)步驟,這三個(gè)步驟也被稱為類的加載或類初始化。
1. 類的加載
類加載是指將類的class文件讀入內(nèi)存,并為之創(chuàng)建一個(gè)java.lang.Class對(duì)象,當(dāng)程序中使用任何類時(shí),系統(tǒng)都會(huì)為之建立一個(gè)java.lang.Class對(duì)象。類的加載由類加載器完成,類加載器通常由JVM提供,JVM提供的這些加載器稱為系統(tǒng)類加載器。也可以通過繼承ClassLoader基類來創(chuàng)建自己的類加載器。
使用不同的類加載器,可以從不同來源加載類的二進(jìn)制數(shù)據(jù),通常的數(shù)據(jù)來源有以下幾種。
- 從本地文件系統(tǒng)加載class文件。
- 從JAR包加載class文件,數(shù)據(jù)庫驅(qū)動(dòng)類的加載就是采用這樣的方式。
- 通過網(wǎng)絡(luò)加載class文件。
- 把一個(gè)Java源文件動(dòng)態(tài)編譯,并執(zhí)行加載。
類加載器通常不用等到首次使用該類時(shí)才加載該類,JVM虛擬機(jī)規(guī)范允許系統(tǒng)預(yù)先加載某些類。
2. 類的連接
當(dāng)類被加載之后,系統(tǒng)會(huì)為之生成一個(gè)對(duì)應(yīng)的Class對(duì)象,接著會(huì)進(jìn)入連接階段,連接階段負(fù)責(zé)把類的二進(jìn)制數(shù)據(jù)合并到JRE中。類的連接階段又可以分為如下三個(gè)階段:
(1) 驗(yàn)證:驗(yàn)證階段用于檢驗(yàn)被加載的類是否有正確的內(nèi)部結(jié)構(gòu),并和其他類協(xié)調(diào)一致。
(2) 準(zhǔn)備:類準(zhǔn)備階段負(fù)責(zé)為類的類變量分配內(nèi)存,并且置默認(rèn)初始值。
(3) 解析:將類的二進(jìn)制數(shù)據(jù)中的符號(hào)引用替換成直接引用。
3. 類的初始化
類的初始化階段虛擬機(jī)主要負(fù)責(zé)對(duì)類進(jìn)行初始化,主要就是對(duì)類變量進(jìn)行初始化。在Java類中對(duì)類變量指定初始值有兩種方式:
①聲明類變量時(shí)指定初始值。
②使用靜態(tài)初始化塊為類變量指定初始值。
類的初始化示例如下:
public class Test{
//聲明變量a時(shí)指定初始值
public static int a=9;
public static int b;
public static int c;
public static{
//在靜態(tài)初始化塊中對(duì)b變量指定初始值
b=89;
}
...
}
上面的代碼中程序?yàn)轭愖兞縜、b都指定了初始值,分別是9和89,而類變量c沒有指定初始值,所以c的初始值是默認(rèn)的初始值為0。
聲明變量時(shí)指定初始值,靜態(tài)初始化塊都會(huì)被當(dāng)成類的初始化語句,JVM按順序執(zhí)行這些語句。例如:
public class Test{
static{
//使用靜態(tài)初始化塊為b變量指定初始值
b=89;
}
public static int a=90;
public static int b=88;
public static int c;
public static void main(String[] args){
System.out.println(a);
System.out.println(b);
System.out.println(c);
}
}
執(zhí)行上面的程序?qū)?huì)看到輸出90、88、0,完成類的加載和連接步驟之后,類變量a、b、c默認(rèn)值是0,類初始化階段先執(zhí)行靜態(tài)初始化塊,對(duì)類變量b進(jìn)行賦值為89,然后執(zhí)行聲明變量時(shí)指定初始值,類變量a聲明時(shí)指定初始值為90,執(zhí)行到聲明類變量b指定初始值時(shí)再次對(duì)類變量b賦值為88,所以b的最終結(jié)果是88,而c變量為默認(rèn)初始值0。
JVM初始化一個(gè)類包含以下幾個(gè)步驟:
①假如這個(gè)類還沒有被加載連接,則程序加載和連接這個(gè)類。
②假如該類的直接父類沒有被初始化,則先初始化其直接父類。
③假如類中有初始化語句,則系統(tǒng)依次執(zhí)行這些初始化語句。
當(dāng)執(zhí)行第二個(gè)步驟時(shí),系統(tǒng)對(duì)直接父類的初始化步驟也遵循上述步驟①-③,如果該類的直接父類還有直接父類,則系統(tǒng)再次重復(fù)這三個(gè)步驟來初始化這個(gè)父類......以此類推。