「找不到類」、「類衝突」、「熱部署失效」—— 90% 的 Java 疑難雜症根源於類加載機制不清。掌握 ClassLoader 委派模型,是從「會用」進階「懂原理」的分水嶺。
| 層級 | 類加載器 | 實現語言 | 加載路徑來源 | 核心職責 |
|---|---|---|---|---|
| 1 (頂層) | Bootstrap ClassLoader | C++ (HotSpot VM 內建) | JAVA_HOME/jre/lib/rt.jar 等核心庫sun.boot.class.path 系統屬性指定 | 加載 Java 核心類 (java.*, javax.*)無 Java 物件引用, getClassLoader() 返回 null |
| 2 (中層) | Extension ClassLoader | Java (sun.misc.Launcher$ExtClassLoader) | JAVA_HOME/jre/lib/ext/*.jarjava.ext.dirs 系統屬性指定 | 加載 JDK 擴充套件 (如 javax.naming, javax.script) |
| 3 (底層) | Application / System ClassLoader | Java (sun.misc.Launcher$AppClassLoader) | CLASSPATH 環境變數-classpath / -cp 啟動參數預設為當前工作目錄 . | 加載應用程式類、第三方依賴 JAR 開發者自定義 ClassLoader 父加載器預設為此 |
protected Class> loadClass(String name, boolean resolve) throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// 1. 檢查是否已加載
Class> c = findLoadedClass(name);
if (c == null) {
try {
// 2. 委派給父加載器
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name); // Bootstrap
}
} catch (ClassNotFoundException e) {
// 3. 父加載器找不到,自己嘗試 findClass
c = findClass(name);
}
}
if (resolve) resolveClass(c);
return c;
}
}
關鍵原則:
public class ClassLoaderDemo {
public static void main(String[] args) {
// 1. 核心類 -> Bootstrap (null)
System.out.println("String ClassLoader: " + String.class.getClassLoader());
// 2. 擴充類 -> Extension
System.out.println("NamingManager ClassLoader: " +
javax.naming.spi.NamingManager.class.getClassLoader());
// 3. 應用類 -> AppClassLoader
System.out.println("ClassLoaderDemo ClassLoader: " + ClassLoaderDemo.class.getClassLoader());
// 4. 自定義類加載器測試
try {
URLClassLoader custom = new URLClassLoader(
new URL[] { new File("custom-lib").toURI().toURL() },
ClassLoaderDemo.class.getClassLoader() // 顯式指定父加載器
);
Class> clazz = custom.loadClass("com.example.CustomClass");
System.out.println("CustomClass ClassLoader: " + clazz.getClassLoader());
} catch (Exception e) {
e.printStackTrace();
}
}
}
| 現象 | 根因 | 解決 |
|---|---|---|
| NoClassDefFoundError / ClassNotFoundException | CLASSPATH 缺失、依賴版本衝突、類加載器隔離 | Maven dependency:tree 排查、打包檢查、統一版本鎖定 |
| LinkageError / ClassCastException | 同一類被不同 ClassLoader 加載多份 | 打破雙親委派 (Tomcat/JBoss 模組化)、共享庫上移至 Common ClassLoader |
| 熱部署/熱替換失效 | 類已加載無法卸載,修改不生效 | 自定義 ClassLoader 重新加載、JRebel/DCEVM、Arthas 動態更新 |
| SPI 機制找不到實現類 | Bootstrap/AppClassLoader 不可見線程上下文類加載器資源 | Thread.currentThread().setContextClassLoader(custom) |
module A requires module B。類加載機制是 Java 動態性、安全性、模組化的基石。理解「三層委派、向上查找、向下隔離」核心邏輯,配合 getClassLoader() 實戰驗證,能快速定位 90% 類加載疑難雜症,也是 JVM 調優與框架源碼閱讀的必修課。