類加載全生命週期
- 加載:尋找並讀取 .class 二進制數據,生成
java.lang.Class 對象
- 鏈接
- 驗證:文件格式、元數據、字節碼語義合法性
- 準備:為 static 欄位分配內存、賦預設值
- 解析:符號引用 → 直接引用(可選,延遲到首次使用)
- 初始化:執行
<clinit>()(static 初始化塊、static 欄位賦值)
ClassLoader 核心方法
| 方法 | 職責 |
loadClass(name, resolve) | 入口點:檢查緩存 → 委派父加載器 → 自定義查找 → defineClass → resolve |
defineClass(bytes) | 字節碼 → Class 對象,含驗證、解析 |
findSystemClass(name) | 從本地路徑(CLASSPATH)加載 |
resolveClass(Class) | 觸發鏈接階段 |
findLoadedClass(name) | 檢查是否已加載,避免重複 |
雙親委派模型

四大標準加載器
| 加載器 | 父加載器 | 加載來源 | 實現語言 |
| Bootstrap | null | sun.boot.class.path (rt.jar 等) | C/C++ (JVM 內建) |
| Extension | Bootstrap | java.ext.dirs | Java |
| System/Application | Extension | CLASSPATH | Java |
| Applet | System | 網絡 CodeBase | Java |
委派流程
// 以 Applet 加載 TestApplet 為例
AppletClassLoader.loadClass("TestApplet")
→ 委派 SystemClassLoader (CLASSPATH 無此類)
→ 委派 ExtensionClassLoader (ext 無此類)
→ 委派 Bootstrap (rt.jar 無此類)
→ 父加載器均失敗,AppletClassLoader 自行從網絡下載 .class
→ 發現父類 java.applet.Applet
→ 再次委派鏈,最終由 Bootstrap 加載 java.applet.Applet
→ 返回已加載的父類 Class 對象
自定義 ClassLoader 實戰範本
public abstract class MultiClassLoader extends ClassLoader {
private Map cache = new HashMap<>();
@Override
public synchronized Class loadClass(String name, boolean resolve)
throws ClassNotFoundException {
// 1. 檢查緩存
Class c = cache.get(name);
if (c != null) return c;
// 2. 委派系統加載器
try {
c = super.findSystemClass(name);
return c;
} catch (ClassNotFoundException ignored) {}
// 3. 自定義來源:資料庫、網絡、加密文件等
byte[] bytes = loadClassBytes(name); // 抽象方法
if (bytes == null) throw new ClassNotFoundException();
// 4. 定義類
c = defineClass(null, bytes, 0, bytes.length);
if (c == null) throw new ClassFormatError();
// 5. 解析與緩存
if (resolve) resolveClass(c);
cache.put(name, c);
return c;
}
protected abstract byte[] loadClassBytes(String name);
}
關鍵約束
- 同一全限定名,同一加載器實例只能返回同一
Class 對象
- 父子加載器對共享類(父類、介面、簽名類型)必須返回同一對象
- 已加載類不可更換加載器(
getClassLoader() 不可變)
典型應用場景
- 熱部署/插件系統:隔離插件類庫,支援版本共存
- 加密類保護:運行時解密字節碼再 defineClass
- 應用服務器:每個 WebApp 獨立 ClassLoader 實現隔離
- JDBC 驅動 SPI:
ServiceLoader 機制依賴線程上下文加載器