類裝載工作由ClassLoader及其子類負責,ClassLoader是一個重要的Java執行時系統組件,它負責在運行時查找和裝入Class字節碼文件。JVM在運行時會產生三個ClassLoader:根裝載器、ExtClassLoader(擴展類裝載器)和AppClassLoader(系統類裝載器)。其中,根裝載器不是ClassLoader的子類,它使用C++編寫,因此我們在Java中看不到它,根裝載器負責裝載JRE的核心類庫,如JRE目標下的rt.jar、charsets.jar等。ExtClassLoader和AppClassLoader都是ClassLoader的子類。其中ExtClassLoader負責裝載JRE目錄ext中的JAR類包;AppClassLoader負責裝載ClassPath路徑下的類包。
- 啟動類加載器(Bootstrap ClassLoader):這個類加載器負責將存放在<JAVA_HOME>\lib目錄中的。啟動類加載器無法被Java程序直接引用,用戶在編寫自定義類加載器時,如果需要把加載請求委派給引導類加載器,那直接使用null代替即可。
- 擴展類加載器(Extension ClassLoader):這個加載器由sun.misc.Launcher$ExtClassLoader實現,它負責加載<JAVA_HOME>\lib\ext目錄中的,或者被java.ext.dirs系統變量所指定的路徑中的所有類庫,開發者可以直接使用擴展類加載器。
- 應用程序類加載器(Application ClassLoader):這個類加載器由sun.misc.Launcher$AppClassLoader實現。由於這個類加載器是ClassLoader中的getSystemClassLoader()方法的返回值,所以一般也稱它為系統類加載器。它負責加載用戶類路徑(ClassPath)上所指定的類庫,開發者可以直接使用這個類加載器,如果應用程序中沒有自定義自己的類加載器,一般情況下這個就是程序中默認的類加載器。
我們的應用程序都是由這3種類加載器互相配合進行加載的,如果有必要,還可以加入自己定義的類加載器。這些類加載器之間的關系一般為:
上圖展示的類加載器之間的這種層次關系,稱為類加載器的雙親委派模型。雙親委派模型要求除了頂層的啟動類加載器外,其余的類加載器都應當有自己的父類加載器。這里類加載器之間的父子關系一般不會以繼承的關系來實現,而是都使用組合關系來復用父加載器的代碼。
這三個類裝載器之間存在父子層級關系,即根裝載器是ExtClassLoader的父裝載器,ExtClassLoader是父類裝載器。默認情況下,使用AppClassLoader裝載應用程序的類,用以下代碼證明:
1 /** 2 * @author zhengbinMac 3 */ 4 public class ClassLoaderTest { 5 6 public static void main(String[] args) { 7 ClassLoader loader = Thread.currentThread().getContextClassLoader(); 8 System.out.println("current loader:" + loader); 9 System.out.println("parent loader:" + loader.getParent()); 10 System.out.println("grandparent loader:" + loader.getParent().getParent()); 11 } 12 /* 13 * output: 14 * current loader:sun.misc.Launcher$AppClassLoader@1b6d3586 15 * parent loader:sun.misc.Launcher$ExtClassLoader@1540e19d 16 * grandparent loader:null // 因為根類裝載器在Java中訪問不到,所有返回null 17 */ 18 }
Thread.currentThread():返回對當前正在執行的線程對象的引用。
getContextClassLoader():返回該線程的上下文 ClassLoader。
通過以上的輸出信息,可以明白,ClassLoader是AppClassLoader,父ClassLoader是ExtClassLoader,祖父ClassLoader是根類裝載器,因為在Java中無法獲得它的句柄,所以直接返回null。
除了JVM默認的三個ClassLoader以外,可以編寫自己的第三方類裝載器,以實現一些特殊的需求。類文件被裝載並解析后,在JVM內將擁有一個對應的java.lang.Class類描述對象,該類的實例都擁有指向這個類描述對象的引用,而類描述對象又擁有指向關聯ClassLoader的引用。如下圖所示:
ClassLoader重要方法
在Java中,ClassLoader是一個抽象類,位於java.lang包中。下面對該類的一些重要接口方法進行介紹:
- Class loadClass(String name)
name參數指定類裝載器需要裝載類的名字,必須使用全限定類名,如com.zhengbin.entity.Student。該方法有一個重載方法loadClass(String name, boolean resolve),resolve參數告訴類裝載器是否需要解析該類。在初始化類之前,應考慮進行類解析的工作,但並不是所有的類都需要解析,如果JVM只需要知道該類是否存在或找出該類的超類,那么就不需要進行解析。
- Class defineClass(String name, byte[] b, int off, int len)
將類文件的字節數組轉換成JVM內部的java.lang.Class對象。字節數組可以從本地文件系統、遠程網絡獲取。name為字節數組對應的全限定類名。
- Class findSystemClass(String name)
從本地文件系統載入Class文件,如果本地文件系統不存在該Class文件,將拋出ClassNotFoundException異常。該方法是JVM默認使用的裝載機制。
- ClassLoader getParent()
獲取類裝載器的父裝載器,除根裝載器外,所有的類裝載器都有且僅有一個父裝載器,ExtClassLoader的父裝載器是根裝載器,因為根裝載器非Java編寫,所以無法獲得,將返回null。