Java 類載入器(ClassLoader)
Java 中的類載入器是用於載入 .class
檔案到 JVM 中的元件,它的核心作用是將位元組碼(.class
檔案)載入到記憶體,並且使它能夠被 JVM 執行。類載入器決定了類的載入順序和載入方式,是 Java 應用程式在執行時執行的重要部分。
1. 類載入器的基本概念
- 類載入器:Java 中的類載入器用於將
.class
檔案載入到 JVM 中,並將其轉化為一個Class
物件,之後該Class
物件可以用於反射等操作。 - 每個類載入器都具備載入類的能力,但它們的載入範圍和載入順序可能不同。
2. 類載入器的型別
2.1 根類載入器(Bootstrap ClassLoader)
- 作用:是最頂層的類載入器,負責載入 Java 核心庫中的類,如
java.lang.*
和java.util.*
。 - 實現:Bootstrap ClassLoader 是由 C++ 實現的,通常由作業系統提供,載入
jre/lib
目錄下的類庫。 - 載入路徑:由環境變數
sun.boot.class.path
指定。
2.2 擴充套件類載入器(Extension ClassLoader)
- 作用:負責載入 JDK 中的擴充套件類庫,主要載入
jre/lib/ext
目錄下的類庫。 - 實現:它是由 Java 實現的,繼承自
ClassLoader
。 - 載入路徑:由
java.ext.dirs
指定。
2.3 系統類載入器(System ClassLoader)
- 作用:也叫應用類載入器,負責載入應用程式的類路徑(
classpath
)下的類。 - 實現:由 Java 實現,通常是使用者應用程式使用的載入器,載入
classpath
中的類。 - 載入路徑:由環境變數
java.class.path
指定。
2.4 自定義類載入器(Custom ClassLoader)
- 作用:開發人員可以自定義類載入器,繼承
ClassLoader
類,重寫findClass()
方法來載入類。 - 應用場景:
- 載入本地檔案、資料庫、網路等外部資源中的類。
- 用於 Web 伺服器(如 Tomcat)中動態載入 JSP 檔案或動態載入外掛等。
3. 類載入的過程
3.1 載入(Load)
- 類載入器根據類名查詢
.class
檔案,並讀取它的位元組流。 - 這個過程會依賴於類載入器的父子關係,父載入器優先載入。
3.2 連結(Linking)
連結分為三個階段:
- 驗證(Verify):檢查位元組碼是否符合 JVM 的要求,防止非法程式碼進入。
- 準備(Prepare):為類的靜態變數分配記憶體並賦預設值。
- 解析(Resolve):將常量池中的符號引用替換為直接引用。
3.3 初始化(Initialize)
- 在類的初始化階段,JVM 執行類的靜態程式碼塊(
static
塊),初始化靜態變數等。
4. 類載入器的雙親委派機制(Parent Delegation Model)
- 雙親委派模型是 Java 類載入器的一個重要特點,它確保了類載入的安全性和穩定性。
- 原理:
- 每個類載入器都有一個父類載入器。當一個類載入請求發生時,子載入器會先將請求傳遞給父載入器,父載入器先嚐試載入該類。如果父載入器載入失敗,子載入器再嘗試載入。
- 這確保了 Java 核心類庫(如
java.lang.*
)始終由根類載入器載入,從而避免了重複載入和潛在的衝突。
5. 自定義類載入器的示例
透過繼承 ClassLoader
類,我們可以建立自己的類載入器,下面是一個簡單的自定義類載入器示例:
public class MyClassLoader extends ClassLoader {
@Override
public Class<?> findClass(String name) throws ClassNotFoundException {
byte[] classData = loadClassData(name); // 從某個源(如檔案、資料庫)獲取位元組資料
return defineClass(name, classData, 0, classData.length);
}
private byte[] loadClassData(String name) {
// 載入類的位元組資料(例如,從檔案或網路中讀取)
return new byte[0]; // 示例返回空位元組陣列
}
}
public class Test {
public static void main(String[] args) throws Exception {
MyClassLoader loader = new MyClassLoader();
Class<?> clazz = loader.loadClass("com.example.MyClass");
Object obj = clazz.newInstance();
}
}
解釋:
- MyClassLoader 繼承自 ClassLoader,重寫 findClass 方法來從某個地方載入類的位元組資料。
- loadClassData 方法是我們用來載入類位元組資料的地方,可以從檔案、資料庫等地方載入。
- defineClass 方法將位元組資料轉換為一個 Class 物件。
6. 類載入器的應用場景
- 動態載入外掛:在應用程式執行時,動態載入新的功能模組或外掛,避免重新啟動應用。
- Web 伺服器:如 Tomcat 會使用不同的類載入器來載入 Web 應用中的類,保證每個應用使用獨立的類載入器。
- 類版本控制:在某些情況下,自定義類載入器可以用來控制不同版本的類載入,避免類衝突。
7. 總結
- 類載入器是 Java 中用於載入 .class 檔案的元件,分為根類載入器、擴充套件類載入器、系統類載入器和自定義類載入器。
- 雙親委派機制保證了類載入的順序和安全性。
- 自定義類載入器為我們提供了靈活性,可以從本地檔案、網路等載入類,支援外掛化開發。