一、什么是類加載器(ClassLoader)
類加載器是 Java 虛擬機中的一部分,負責將 .class
文件加載到 JVM 內存中,生成對應的 Class
對象。
Java 程序中所有的類在使用前都必須通過類加載器加載進 JVM,才能被執行。
二、類加載器的作用
- 加載
.class
文件到內存中。 - 將字節碼轉換為 JVM 能識別的
Class
對象。 - 實現類的 命名空間隔離。
- 支持 模塊化開發(如插件機制、自定義業務模塊加載)。
三、類加載器的分類(JVM 內置 + 用戶自定義)
1. 啟動類加載器(Bootstrap ClassLoader)
- 作用: 加載 JVM 的核心類庫,如
java.lang.*
、java.util.*
等。 - 加載路徑:
JAVA_HOME/lib
目錄中的類(如rt.jar
)。 - 實現: 由 C/C++ 實現,是 JVM 的一部分。
- 特點: 不是 Java 類,不能被直接引用或操作。
2. 擴展類加載器(Extension ClassLoader)
- 作用: 加載 Java 擴展類庫。
- 加載路徑:
JAVA_HOME/lib/ext/
目錄或由java.ext.dirs
系統變量指定的路徑。 - 父加載器: Bootstrap ClassLoader。
- 類名:
sun.misc.Launcher$ExtClassLoader
。
3. 應用類加載器 / 系統類加載器(Application ClassLoader)
- 作用: 加載用戶類路徑(classpath)下的類文件。
- 加載路徑: 當前應用的
classpath
(如 jar 包或類文件所在目錄)。 - 父加載器: Extension ClassLoader。
- 類名:
sun.misc.Launcher$AppClassLoader
。
4. 自定義類加載器(Custom ClassLoader)
-
作用: 開發者可以繼承
java.lang.ClassLoader
實現自己的加載邏輯。 -
使用場景:
- 熱部署
- 模塊化(如 OSGi)
- 插件系統
- 加密 class 文件
-
常見方式:
- 繼承
ClassLoader
并重寫findClass()
方法 - 調用
defineClass()
定義類對象
- 繼承
四、類加載器的層次結構圖
┌──────────────────────────┐
│ Bootstrap ClassLoader │
│ (C++實現, 加載核心類庫) │
└──────────┬───────────────┘↓
┌──────────────────────────┐
│ Extension ClassLoader │
│ (加載 ext 目錄類) │
└──────────┬───────────────┘↓
┌──────────────────────────┐
│ Application ClassLoader │
│ (加載classpath下類) │
└──────────┬───────────────┘↓
┌──────────────────────────┐
│ 自定義 ClassLoader │
│ (可指定加載路徑/策略) │
└──────────────────────────┘
五、雙親委派機制(Parent Delegation Model)
定義:
類加載器在加載類時,首先會 將加載請求委托給父加載器,由頂層的 Bootstrap 開始查找,只有在父加載器找不到時,才由當前加載器加載。
加載流程:
- 當前類加載器收到類加載請求。
- 委托給父類加載器。
- 如果父類無法加載,才由當前加載器嘗試加載。
優點:
- 避免類的重復加載。
- 防止用戶自定義類覆蓋 JDK 核心類(如
java.lang.String
)。
舉例說明:
public class Test {public static void main(String[] args) {System.out.println(String.class.getClassLoader()); // null(Bootstrap)System.out.println(Test.class.getClassLoader()); // AppClassLoader}
}
六、自定義類加載器示例
public class MyClassLoader extends ClassLoader {@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {byte[] data = loadClassData(name); // 從文件或網絡中讀取字節數組return defineClass(name, data, 0, data.length);}
}
七、線程上下文類加載器(ContextClassLoader)
定義:
- 每個線程可以設置自己的類加載器,用于動態加載類或資源。
- 默認是
Application ClassLoader
。
用途:
- 在 Java SPI(Service Provider Interface)中尤為重要。
- 解決雙親委派帶來的靈活性限制。
Thread.currentThread().setContextClassLoader(new MyClassLoader());
八、類加載器相關方法(Java API)
方法 | 說明 |
---|---|
loadClass(String name) | 加載類(會委托給父類) |
findClass(String name) | 查找類(自定義類加載核心) |
defineClass(...) | 將字節數組轉為 Class 對象 |
getParent() | 獲取父加載器 |
getClassLoader() | 獲取當前類的加載器 |
九、面試常問點總結
問題 | 要點回答 |
---|---|
什么是類加載器? | 將 .class 加載進內存,生成 Class 對象。 |
JVM 有哪些類加載器? | 啟動類、擴展類、應用類、自定義類加載器。 |
雙親委派模型是什么? | 加載委托給父加載器,避免重復 & 保證安全性。 |
如何打破雙親委派? | 重寫 loadClass() 不委托父類。 |
自定義類加載器的用途? | 插件、加密、安全、熱更新、動態部署等。 |