類的加載指的是將類的.class文件中的二進制數(shù)據(jù)讀入到內存中,將其放在運行時數(shù)據(jù)區(qū)的方法區(qū)內,,然后在堆區(qū)創(chuàng)建一個java.lang.Class對象,用來封裝類在方法區(qū)內的數(shù)據(jù)結構,。類的加載的最終產(chǎn)品是位于堆區(qū)中的Class對象,,Class對象封裝了類在方法區(qū)內的數(shù)據(jù)結構,并且向Java程序員提供了訪問方法區(qū)內的數(shù)據(jù)結構的接口,。 看如下一個例子: 運行結果為:從如上代碼可以看到,,我們總共獲取到了兩個classloader。而實際上并非只有兩個(啟動類加載器是不可見的),。如下圖為類加載的結構圖:注意:這里父類加載器并不是通過繼承關系來實現(xiàn)的,,而是采用組合實現(xiàn)的。從開發(fā)人員的角度來說,,一般我們可以區(qū)分為4類類加載器,,分別為:啟動類加載器,擴展類加載器,,應用類加載器,,自定義類加載器。 - 啟動類加載器:Bootstrap ClassLoader,,負責加載存放在JDK\jre\lib(JDK代表JDK的安裝目錄,,下同)下,或被-Xbootclasspath參數(shù)指定的路徑中的,,并且能被虛擬機識別的類庫(如rt.jar,,所有的java.*開頭的類均被Bootstrap ClassLoader加載)。啟動類加載器是無法被Java程序直接引用的,。
- 擴展類加載器:Extension ClassLoader,,該加載器由sun.misc.Launcher$ExtClassLoader實現(xiàn),它負責加載DK\jre\lib\ext目錄中,,或者由java.ext.dirs系統(tǒng)變量指定的路徑中的所有類庫(如javax.*開頭的類),,開發(fā)者可以直接使用擴展類加載器。
- 應用程序類加載器:該類加載器由sun.misc.Launcher$AppClassLoader來實現(xiàn),,它負責加載用戶類路徑(ClassPath)所指定的類,,開發(fā)者可以直接使用該類加載器,,如果應用程序中沒有自定義過自己的類加載器,一般情況下這個就是程序中默認的類加載器,。
- 自定義類加載器:用戶自定義的類加載器,。
注意:對于JVM來說,它只區(qū)分兩類類加載器 - 啟動類加載器:它是使用C++編寫的(僅限hotspot),,它是java虛擬機的一部分
- 其他所有的類加載器,。它是使用java寫的。這些類加載器都由Java語言實現(xiàn),,獨立于虛擬機之外,,并且全部繼承自抽象類java.lang.ClassLoader,這些類加載器需要由啟動類加載器加載到內存中之后才能去加載其他的類,。
- 全盤負責,,當一個類加載器負責加載某個Class時,該Class所依賴的和引用的其他Class也將由該類加載器負責載入,,除非顯示使用另外一個類加載器來載入
- 父類委托,,先讓父類加載器試圖加載該類,只有在父類加載器無法加載該類時才嘗試從自己的類路徑中加載該類
- 緩存機制,,緩存機制將會保證所有加載過的Class都會被緩存,,當程序中需要使用某個Class時,類加載器先從緩存區(qū)尋找該Class,,只有緩存區(qū)不存在,,系統(tǒng)才會讀取該類對應的二進制數(shù)據(jù),并將其轉換成Class對象,,存入緩存區(qū),。這就是為什么修改了Class后,必須重啟JVM,,程序的修改才會生效
雙親委派模型的工作流程是:如果一個類加載器收到了類加載的請求,,它首先不會自己去嘗試加載這個類,而是把請求委托給父加載器去完成,,依次向上,,因此,所有的類加載請求最終都應該被傳遞到頂層的啟動類加載器中,,只有當父加載器在它的搜索范圍中沒有找到所需的類時,,即無法完成該加載,子加載器才會嘗試自己去加載該類,。其加載機制如下: - 當AppClassLoader加載一個class時,,它首先不會自己去嘗試加載這個類,而是把類加載請求委派給父類加載器ExtClassLoader去完成,。
- 當ExtClassLoader加載一個class時,,它首先也不會自己去嘗試加載這個類,,而是把類加載請求委派給BootStrapClassLoader去完成。
- 如果BootStrapClassLoader加載失?。ɡ缭?JAVA_HOME/jre/lib里未查找到該class),,會使用ExtClassLoader來嘗試加載;
- 若ExtClassLoader也加載失敗,,則會使用AppClassLoader來加載,,如果AppClassLoader也加載失敗,則會報出異常ClassNotFoundException,。
可見,,java類加載器是層級結構的,當需要加載一個類的時候,,它實際上使用類似委托的方式委托其父加載器去加載該類,,當父類加載器無法找到該類的時候,才使用當前類加載器加載,。
|