你瞭解 Java 的類載入器嗎?

Eiffelzero發表於2024-12-10

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)

連結分為三個階段:

  1. 驗證(Verify):檢查位元組碼是否符合 JVM 的要求,防止非法程式碼進入。
  2. 準備(Prepare):為類的靜態變數分配記憶體並賦預設值。
  3. 解析(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 檔案的元件,分為根類載入器、擴充套件類載入器、系統類載入器和自定義類載入器。
  • 雙親委派機制保證了類載入的順序和安全性。
  • 自定義類載入器為我們提供了靈活性,可以從本地檔案、網路等載入類,支援外掛化開發。

相關文章