類加載器是 JVM 實現動態性的基石。本文系統梳理三大系統類加載器層次、獲取方式、雙親委派工作流程、源碼級實現、以及歷史上三次重大「破壞」場景(JNDI、OSGi、熱部署)。
%JAVA_HOME%/lib(如 rt.jar)及 -Xbootclasspath 指定路徑。java.lang.ClassLoader,獨立於 JVM 外部。| 加載器 | 實現類 | 加載路徑 | 獲取方式 |
|---|---|---|---|
| Extension ClassLoader | sun.misc.Launcher$ExtClassLoader | %JAVA_HOME%/lib/ext、java.ext.dirs | ClassLoader.getSystemClassLoader().getParent() |
| Application ClassLoader | sun.misc.Launcher$AppClassLoader | 用戶 classpath(-cp/Manifest) | ClassLoader.getSystemClassLoader()(默認) |
父子關係:AppClassLoader.parent = ExtClassLoader;ExtClassLoader.parent = null(非 Bootstrap)。Bootstrap 不在 Java 層次體系中,無法直接引用。
// 1. 當前類的類加載器
this.getClass().getClassLoader();
// 2. 當前線程上下文類加載器(支援 JNDI/SPI)
Thread.currentThread().getContextClassLoader();
// 3. 系統類加載器(AppClassLoader)
ClassLoader.getSystemClassLoader();
// 4. 類字面量 .class 隱含加載器
String.class.getClassLoader(); // 返回 null (Bootstrap)
Class.forName()、ClassLoader.loadClass()、自定義 findClass()。findClass 加載。java.lang.String 無法被 Bootstrap 加載)。java.lang.ClassLoader.loadClass)protected Class> loadClass(String name, boolean resolve)
throws ClassNotFoundException {
synchronized (getClassLoadingLock(name)) {
// 1. 檢查已加載
Class> c = findLoadedClass(name);
if (c == null) {
// 2. 委派父加載器
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClassOrNull(name);
}
// 3. 父加載失敗,自身 findClass
if (c == null) {
c = findClass(name);
}
}
if (resolve) resolveClass(c);
return c;
}
}
loadClass,JDK 1.2 引入委派模型時保留向下兼容,建議改寫 findClass。Thread.setContextClassLoader(AppClassLoader),父加載器通過子加載器反向加載。Class.forName("全限定名")this.getClass()(實例方法)String.class(類字面量)Integer.TYPE / int.class