類加載器(classloader)就是在系統運作過程中動态的将位元組碼檔案加載到 jvm 中的工具,基于這個工具的整套類加載流程,我們稱作類加載機制。我們在 ide 中編寫的都是源代碼檔案,以字尾名為 .java 的檔案形式存在于磁盤上,通過編譯後生成字尾名為 .class 的位元組碼檔案,classloader 加載的就是這些位元組碼檔案。
java 預設提供了三個 classloader,分别是根加載器(bootstrapclassloader)、擴充類加載器(extclassloader)、應用類加載器(appclassloader),依次前者分别是後者的「父加載器」。父加載器不是「父類」,三者之間沒有繼承關系,隻是因為類加載的流程使三者之間形成了父子關系。
還有一種是使用者自定義類加載器(java.lang.classloader的子類)。從java 2(jdk 1.2)開始,類加載過程采取了父親委托機制(pdm),pdm更好的保證了java平台的安全性。在該機制中,jvm自帶的bootstrapclassloader是根加載器,其他的加載器都有且僅有一個父類加載器。類的加載首先請求父類加載器加載,父類加載器無能為力時才由其子類加載器自行加載。jvm不會向java程式提供對bootstrapclassloader的引用。下面是關于幾個類加載器的說明:
bootstrapclassloader:根加載器,它是脫離 java 語言,使用 c/c++ 編寫的類加載器,是以當你嘗試使用 extclassloader 的執行個體調用 getparent() 方法擷取其父加載器時會得到一個 null 值。

根加載器會預設加載系統變量 sun.boot.class.path 指定的類庫(jar 檔案和 .class 檔案),預設是 $jre_home/lib 下的類庫,如 rt.jar、resources.jar 等,具體可以輸出該環境變量的值來檢視。
除了加載這些預設的類庫外,也可以使用 jvm 參數 -xbootclasspath/a 來追加額外需要讓根加載器加載的類庫。比如我們自定義一個 com.ganpengyu.boot.dateutils 類來讓根加載器加載。
我們将其制作成一個名為 gpy-boot 的 jar 包放到 /users/yu/desktop/lib 下,然後寫一個測試類去嘗試加載 dateutils。
運作這個測試類:
可以看到輸出為 true,也就是說加載 com.enjoy.boot.dateutils 的類加載器在 java 中無法獲得其引用,而任何類都必須通過類加載器加載才能被使用,是以推斷出這個類是被 bootstrapclassloader 加載的,也證明了 -xbootclasspath/a 參數确實可以追加需要被根加載器額外加載的類庫。
總之,對于 bootstrapclassloader 這個根加載器我們需要知道三點:
根加載器使用 c/c++ 編寫,我們無法在 java 中獲得其執行個體
根加載器預設加載系統變量 sun.boot.class.path 指定的類庫
可以使用 -xbootclasspath/a 參數追加根加載器的預設加載類庫
extclassloader:擴充類加載器,它是一個使用 java 實作的類加載器(sun.misc.launcher.extclassloader),用于加載系統所需要的擴充類庫。預設加載系統變量 java.ext.dirs 指定位置下的類庫,通常是 $jre_home/lib/ext 目錄下的類庫。
我們可以在啟動時修改java.ext.dirs 變量的值來修改擴充類加載器的預設類庫加載目錄,但通常并不建議這樣做。如果我們真的有需要擴充類加載器在啟動時加載的類庫,可以将其放置在預設的加載目錄下。
總之,對于 extclassloader 這個擴充類加載器我們需要知道兩點:
擴充類加載器是使用 java 實作的類加載器,我們可以在程式中獲得它的執行個體并使用。
通常不建議修改java.ext.dirs 參數的值來修改預設加載目錄,如有需要,可以将要加載的類庫放到這個預設目錄下。
appclassloader:應用類加載器,它和 extclassloader 一樣,也是使用 java 實作的類加載器(sun.misc.launcher.appclassloader)。它的作用是加載應用程式 classpath 下所有的類庫。它是應用最廣泛的類加載器,是我們最常打交道的類加載器,我們在程式中調用的很多 getclassloader() 方法傳回的都是它的執行個體。在我們自定義類加載器時如果沒有特别指定,那麼我們自定義的類加載器的預設父加載器也是這個應用類加載器。
總之,對于 appclassloader 這個應用類加載器我們需要知道三點:
應用類加載器是使用 java 實作的類加載器,負責加載應用程式 classpath 下的類庫。
應用類加載器是和我們最常打交道的類加載器。
沒有特别指定的情況下,自定義類加載器的父加載器就是應用類加載器。
自定義類加載器:
除了上述三種 java 預設提供的類加載器外,我們還可以通過繼承 java.lang.classloader 來自定義一個類加載器。如果在建立自定義類加載器時沒有指定父加載器,那麼預設使用 appclassloader 作為父加載器。
上文已經提到過 bootstrapclassloader 是一個使用 c/c++ 編寫的類加載器,它已經嵌入到了 jvm 的核心之中。當 jvm 啟動時,bootstrapclassloader 也會随之啟動并加載核心類庫。當核心類庫加載完成後,bootstrapclassloader 會建立 extclassloader 和 appclassloader 的執行個體,兩個 java 實作的類加載器将會加載自己負責路徑下的類庫,這個過程我們可以在 sun.misc.launcher 中窺見。
jvm中類的加載是由類加載器(classloader)和它的子類來實作的。java中的類加載器是一個重要的java運作時系統元件,它負責在運作時查找和裝入類檔案中的類。
由于java的跨平台性,經過編譯的java源程式并不是一個可執行程式,而是一個或多個類檔案。當java程式需要使用某個類時,jvm會確定這個類已經被加載、連接配接(驗證、準備和解析)和初始化。
一、類的加載是指把類的.class檔案中的資料讀入到記憶體中,通常是建立一個位元組數組讀入.class檔案,然後産生與所加載類對應的class對象。加載完成後class對象還不完整,是以此時的類還不可用。
二、當類被加載後就進入連接配接階段,這一階段包括驗證、準備(為靜态變量配置設定記憶體并設定預設的初始值)和解析(将符号引用替換為直接引用)三個步驟。
三、最後jvm對類進行初始化,包括:1)如果類存在直接的父類并且這個類還沒有被初始化,那麼就先初始化父類;2)如果類中存在初始化語句,就依次執行這些初始化語句。