ClassLoader(類別載入器) 是 Java 執行環境的核心組件,負責將 .class 位元碼從各種來源(檔案系統、網路、JAR、資料庫等)讀入 JVM 記憶體,轉換為 java.lang.Class 物件,供程式執行使用。Java 的動態載入特性、熱部署、模組化框架(如 OSGi、Spring 模組)皆建立於此機制之上。
<clinit>() 方法(靜態區塊、靜態欄位賦值)。這是個經典「雞生蛋、蛋生雞」問題。JVM 啟動時會建立三層內建載入器(Bootstrap → Extension → Application),它們由 C++ 實作(Bootstrap)或 Java 實作但由上層載入,形成啟動鏈:
<JAVA_HOME>/lib 核心類別(rt.jar 等),無父載入器。<JAVA_HOME>/lib/ext 或 java.ext.dirs 指定目錄。classpath 指定的應用程式類別,也是 ClassLoader.getSystemClassLoader() 返回的實例。使用者自訂 ClassLoader 必須繼承 java.lang.ClassLoader,其父載入器預設為 Application ClassLoader,形成樹狀階層。
載入請求先向上委派給父載入器,只有父載入器找不到時,子載入器才嘗試自行載入。優點:
protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
Class> c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) c = parent.loadClass(name, false);
else c = findBootstrapClassOrNull(name);
} catch (ClassNotFoundException e) { /* ignore */ }
if (c == null) c = findClass(name); // 自己載入
}
if (resolve) resolveClass(c);
return c;
}
}
Thread.currentThread().getContextClassLoader() 反向載入。public class MyClassLoader extends ClassLoader {
private final String basePath;
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)) {
return is.readAllBytes();
} catch (IOException e) {
return null;
}
}
}
ByteBuddy、CGLIB 等庫底層即用自訂載入器定義新類別。掌握 ClassLoader 機制,是進階 Java 開發、框架原理分析與疑難雜症排查的關鍵鑰匙。