共計 3412 個字符,預計需要花費 9 分鐘才能閱讀完成。
本篇文章為大家展示了 Java 的 ClassLoader 如何理解,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。
講解一下 Java 的 ClassLoader。
關于 ClassLoader,相信大家用的不是很多,但是在面試中可能會被經常問到。所以我這里整理了關于它的一些相關用法。
ClassLoader 是 Java 提供的類加載器,用來加載 Java 類到 Java 虛擬機中的一種加載器。
Java 程序(class 文件)并不是本地的可執行程序。當運行 Java 程序時,首先運行 JVM(Java 虛擬機),然后再把 Java class 加載到 JVM 里頭運行,負責加載 Java class 的這部分就叫做 Class Loader。
JVM 本身包含了一個 ClassLoader 稱為 Bootstrap ClassLoader,和 JVM 一樣,BootstrapClassLoader 是用本地代碼實現的,它負責加載核心 JavaClass(即所有 java.* 開頭的類)。另外 JVM 還會提供兩個 ClassLoader,它們都是用 Java 語言編寫的,由 BootstrapClassLoader 加載;其中 Extension ClassLoader 負責加載擴展的 Javaclass(例如所有 javax.* 開頭的類和存放在 JRE 的 ext 目錄下的類),ApplicationClassLoader 負責加載應用程序自身的類。
當運行一個程序的時候,JVM 啟動,運行 bootstrapclassloader,該 ClassLoader 加載 java 核心 API(ExtClassLoader 和 AppClassLoader 也在此時被加載),然后調用 ExtClassLoader 加載擴展 API,最后 AppClassLoader 加載 CLASSPATH 目錄下定義的 Class,這就是一個程序最基本的加載流程。
了解了上面的流程后,我們再通過具體的代碼來看看類是如何被加載的。
1
2
3
4
5
6
7
8
9
package com.neo.classloader;
public class ClassLoaderTest {
public static void main(String[] args) {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
System.out.println(loader);
System.out.println(loader.getParent());
System.out.println(loader.getParent().getParent());
}
}
運行后,輸出結果:
1
2
3
sun.misc.Launcher$AppClassLoader@64fef26a
sun.misc.Launcher$ExtClassLoader@1ddd40f3
null
從上面的結果可以看出,并沒有獲取到 ExtClassLoader 的父 Loader,原因是 Bootstrap Loader(引導類加載器)是用 C 語言實現的,找不到一個確定的返回父 Loader 的方式,于是就返回 null。這幾種類加載器的層次關系如下圖所示:
注意:這里父類加載器并不是通過繼承關系來實現的,而是采用組合實現的。
站在 Java 虛擬機的角度來講,只存在兩種不同的類加載器:啟動類加載器:它使用 C ++ 實現(這里僅限于 Hotspot,也就是 JDK1.5 之后默認的虛擬機,有很多其他的虛擬機是用 Java 語言實現的),是虛擬機自身的一部分;所有其它的類加載器:這些類加載器都由 Java 語言實現,獨立于虛擬機之外,并且全部繼承自抽象類 java.lang.ClassLoader,這些類加載器需要由啟動類加載器加載到內存中之后才能去加載其他的類。
站在 Java 開發人員的角度來看,類加載器可以大致劃分為以下三類:
啟動類加載器:Bootstrap ClassLoader,負責加載存放在 JDK\jre\lib(JDK 代表 JDK 的安裝目錄,下同) 下,或被 -Xbootclasspath 參數指定的路徑中的,并且能被虛擬機識別的類庫(如 rt.jar,所有的 java. 開頭的類均被 Bootstrap ClassLoader 加載)。啟動類加載器是無法被 Java 程序直接引用的。
擴展類加載器:Extension ClassLoader,該加載器由 sun.misc.Launcher$ExtClassLoader 實現,它負責加載 JDK\jre\lib\ext 目錄中,或者由 java.ext.dirs 系統變量指定的路徑中的所有類庫(如 javax. 開頭的類),開發者可以直接使用擴展類加載器。
應用程序類加載器:Application ClassLoader,該類加載器由 sun.misc.Launcher$AppClassLoader 來實現,它負責加載用戶類路徑(ClassPath)所指定的類,開發者可以直接使用該類加載器,如果應用程序中沒有自定義過自己的類加載器,一般情況下這個就是程序中默認的類加載器。
應用程序都是由這三種類加載器互相配合進行加載的,如果有必要,我們還可以加入自定義的類加載器。因為 JVM 自帶的 ClassLoader 只是懂得從本地文件系統加載標準的 java class 文件,因此如果編寫了自己的 ClassLoader,便可以做到如下幾點:
1、在執行非置信代碼之前,自動驗證數字簽名。
2、動態地創建符合用戶特定需要的定制化構建類。
3、從特定的場所取得 java class,例如數據庫中和網絡中。
JVM 類加載機制
全盤負責,當一個類加載器負責加載某個 Class 時,該 Class 所依賴的和引用的其他 Class 也將由該類加載器負責載入,除非顯示使用另外一個類加載器來載入
父類委托,先讓父類加載器試圖加載該類,只有在父類加載器無法加載該類時才嘗試從自己的類路徑中加載該類
緩存機制,緩存機制將會保證所有加載過的 Class 都會被緩存,當程序中需要使用某個 Class 時,類加載器先從緩存區尋找該 Class,只有緩存區不存在,系統才會讀取該類對應的二進制數據,并將其轉換成 Class 對象,存入緩存區。這就是為什么修改了 Class 后,必須重啟 JVM,程序的修改才會生效
類加載有三種方式:
1、命令行啟動應用時候由 JVM 初始化加載
2、通過 Class.forName() 方法動態加載
3、通過 ClassLoader.loadClass() 方法動態加載
例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
package com.xttblog.classloader;
public class loaderTest {
public static void main(String[] args) throws ClassNotFoundException {
ClassLoader loader = HelloWorld.class.getClassLoader();
System.out.println(loader);
loader.loadClass(Test2);
}
}
Test2.java
1
2
3
4
5
public class Test2 {
static {
System.out.println(靜態初始化塊執行了!);
}
}
分別切換加載方式,會有不同的輸出結果。
Class.forName() 和 ClassLoader.loadClass() 區別
Class.forName():將類的.class 文件加載到 jvm 中之外,還會對類進行解釋,執行類中的 static 塊;
ClassLoader.loadClass():只干一件事情,就是將.class 文件加載到 jvm 中,不會執行 static 中的內容, 只有在 newInstance 才會去執行 static 塊。
Class.forName(name, initialize, loader) 帶參函數也可控制是否加載 static 塊。并且只有調用了 newInstance() 方法采用調用構造函數,創建類的對象。
總結:Java 類的加載機制和 ClassLoader 看似復雜,實際上卻很簡單。通過閱讀源碼或結合運行示例就能更好的理解了。
上述內容就是 Java 的 ClassLoader 如何理解,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注丸趣 TV 行業資訊頻道。