類加載過程
類加載可以分為加載蚁滋、連接冯事、初始化3個部分
加載
加載過程是指查找并加載類的二進(jìn)制數(shù)據(jù)焦匈,加載class文件的方式可以有以下幾種
- 本地class文件
- 網(wǎng)絡(luò)下載的class文件
- jar或zip等歸檔文件中加載class文件
- 將java源文件動態(tài)編譯的class文件(動態(tài)代理)
- 特殊的數(shù)據(jù)庫中提取class文件
類加載的最終產(chǎn)品就是位于內(nèi)存中的Class對象。Class對象封裝了類在方法區(qū)內(nèi)的數(shù)據(jù)結(jié)構(gòu)并提供了能夠訪問方法區(qū)內(nèi)這些數(shù)據(jù)結(jié)構(gòu)的接口昵仅。
Java提供了兩種類加載器
Java虛擬機(jī)自帶的類加載器
- Bootstrap ClassLoader(由C++編寫)
- Extension ClassLoader(純Java的加載器)
- App/System ClassLoader(純Java的加載器)
用戶自定義的類加載器
1.java.lang.ClassLoader的子類
連接
連接就是對已經(jīng)讀入內(nèi)存的類的二進(jìn)制數(shù)據(jù)合并到虛擬機(jī)的運(yùn)行時環(huán)境中去缓熟,連接又分為3個步驟,分別是
-
驗(yàn)證
驗(yàn)證是對二進(jìn)制數(shù)據(jù)的合法性進(jìn)行校驗(yàn)摔笤,具體是指如下幾點(diǎn)
- 類文件結(jié)構(gòu)檢查
- 語義檢查
- 字節(jié)碼驗(yàn)證
- 兼容性驗(yàn)證
-
準(zhǔn)備
為類的靜態(tài)變量申請內(nèi)存够滑,并為其附上其類型的默認(rèn)值。(0吕世,false彰触,null等)
-
解析
在類型的常量池中尋找類、接口命辖、字段和方法的符號應(yīng)用轉(zhuǎn)換為直接應(yīng)用
初始化
初始化是指為類的靜態(tài)變量設(shè)置正確的初始值况毅,即在代碼中真正設(shè)置的值分蓖。
類初始化步驟:
- 假如該類還未加載和連接,則先進(jìn)行加載和連接
- 加入該類存在父類尔许,且父類還未被初始化時則先初始化父類
- 初始化是按照代碼編寫順序依次執(zhí)行 的代碼么鹤。
類的初始化時機(jī)
類的初始化時機(jī)與類的使用方式密切相關(guān)
Java程序?qū)︻惖膬煞N使用方式
- 主動使用
- 被動使用
其中主動使用會引發(fā)上面的初始化操作。Java虛擬機(jī)要求每個類或接口在其被首次主動使用時才初始化類或接口味廊。
主動使用某個類或接口的七種情況
- 創(chuàng)建實(shí)例(new Object)
- 調(diào)用類的靜態(tài)方法
- 對類的靜態(tài)屬性get或set
- 反射(Class.forName蒸甜,需要注意的是在Class.forName時有一個initialize參數(shù)可以控制是否需要初始化,默認(rèn)為true余佛。)
- 初始化一個類的子類
- Java虛擬機(jī)啟動時被標(biāo)記為啟動類的類
- JDK1.7提供的對動態(tài)語言支持的三個句柄對應(yīng)的類沒初始化時
除了以上7種情況外的都是對類的被動使用柠新,被動使用是不會導(dǎo)致對類的初始化的。
另外訪問類的靜態(tài)屬性時有一種特殊情況不會造成類的主動使用衙熔,示例代碼如下
public class Sample{
public static final int a = 1;
public static final int b = new Random(3).nextInt(3);
}
public class Main {
public static void main(String args[]){
// 這里直接訪問Sample的靜態(tài)屬性a登颓,但并不會初始化Sample類,因?yàn)閷傩詀是final的红氯,因此在編譯期編譯器會將a的值放入Main類的常量池中框咙,在運(yùn)行時并不會去引用Sample類
System.out.println(Sample.a);
// 這里對Sample的靜態(tài)屬性b的直接訪問會造成Sample的初始化,因?yàn)殡m然b也是被final修飾的痢甘,但b的值需要在運(yùn)行時才能確定喇嘱,因此還是需要對Sample類進(jìn)行初始化操作
System.out.println(Sample.b);
}
}
調(diào)用ClassLoader的loadClass并不是對類的主動使用,因此不會進(jìn)行初始化塞栅。
接口的初始化時機(jī)
上文已經(jīng)說過者铜,當(dāng)Java虛擬機(jī)初始化一個類時,要求其所有父類都已經(jīng)被初始化放椰,但此規(guī)則并不適用于接口
針對接口來說遵循如下兩種規(guī)則
- 在初始化一個類時并不會初始化其實(shí)現(xiàn)的接口
- 在初始化一個字接口時并不會對其父接口進(jìn)行初始化
因此作烟,一個父接口并不會因?yàn)槠渥纸涌诨驅(qū)崿F(xiàn)類的初始化而初始化,只有當(dāng)程序首次主動使用特定接口的靜態(tài)變量時才會對其進(jìn)行初始化砾医。