將 .class 位元組碼載入 JVM 方法區,生成 java.lang.Class 實例,供程式使用。載入時機:靜態(new、static 存取)或動態(Class.forName()、ClassLoader.loadClass())。
| 加載器 | 實現語言 | 載入範圍 | 父加載器 |
|---|---|---|---|
| Bootstrap ClassLoader | C++ (JVM 內建) | <JAVA_HOME>/lib 核心庫 (rt.jar 等) | null (頂層) |
| Extension ClassLoader | Java | <JAVA_HOME>/lib/ext 擴展庫 | Bootstrap |
| Application ClassLoader | Java | Classpath (-cp) 指定目錄/JAR | Extension |
JVM 啟動 → Bootstrap 載入核心 API → Extension 載入擴展 → AppClassLoader 載入應用類 → 用戶線程運行。
protected synchronized Class> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
Class> c = findLoadedClass(name); // 1. 快取檢查
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false); // 2. 委派父加載器
} else {
c = findBootstrapClassOrNull(name); // 3. 委派 Bootstrap
}
} catch (ClassNotFoundException e) {
c = findClass(name); // 4. 自己載入
}
}
if (resolve) resolveClass(c);
return c;
}
parent.loadClass();無父加載器 → 委派 Bootstrap。ClassNotFoundException) → 呼叫自身 findClass() 載入。resolve 為 true 時進行連結(驗證、準備、解析)。java.lang.String、java.lang.System 等核心類竄改行為。Bootstrap 優先載入官方類,用戶類永無機會覆蓋。ServiceLoader 機制)。defineClass。public class MyClassLoader extends ClassLoader {
private final String basePath; // 類文件根目錄或 URL
public MyClassLoader(String basePath) {
this.basePath = basePath;
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
byte[] classData = loadClassData(name); // 從檔案/網路/資料庫讀取位元組碼
if (classData == null) throw new ClassNotFoundException(name);
return defineClass(name, classData, 0, classData.length); // 核心:定義類
}
private byte[] loadClassData(String name) {
String path = basePath + name.replace('.', '/') + ".class";
try (InputStream is = new FileInputStream(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
byte[] buf = new byte[1024]; int len;
while ((len = is.read(buf)) != -1) baos.write(buf, 0, len);
return baos.toByteArray();
} catch (IOException e) { return null; }
}
}
使用:new MyClassLoader("D:/myclasses/").loadClass("com.demo.MyService")。
| 方法 | 簽名 | 是否初始化 | 典型用途 |
|---|---|---|---|
| Class.forName | forName(String name) | 是 (執行 static 區塊) | JDBC Driver 註冊、SPI 載入 |
| Class.forName | forName(name, initialize, loader) | 可控制 | 精確控制初始化時機 |
| ClassLoader.loadClass | loadClass(String name) | 否 (resolve=false) | 僅載入不觸發初始化 |
關鍵差異: JDBC Driver 需在 static 區塊呼叫 DriverManager.registerDriver(),必須用 Class.forName() 觸發初始化;若用 loadClass() 則不會註冊,導致 No suitable driver。
ClassLoader 是 Java 動態性的基石。掌握 雙親委派流程、三大內建加載器分工、打破委派的典型場景、自定義加載器寫法、forName vs loadClass 初始化語義差異,即可駕馭類隔離、熱插拔、加密保護、框架 SPI 等進階主題。源碼閱讀建議從 java.lang.ClassLoader 與 sun.misc.Launcher 入手。