Bootstrap ClassLoader 由 C++ 实现,加载 $JAVA_HOME/jre/lib 下的核心类(如 java.lang.Object),不继承 java.lang.ClassLoader,故 String.class.getClassLoader() 返回 null,且无法通过 Java 代码直接引用或获取。

Bootstrap ClassLoader 加载什么、为什么不能被 Java 代码直接引用
Bootstrap ClassLoader 是由 C++ 实现的,负责加载 $JAVA_HOME/jre/lib 下的核心类(如 java.lang.Object、java.util.ArrayList),它不继承自 java.lang.ClassLoader,因此任何 Java 代码中调用 String.class.getClassLoader() 都会返回 null。
常见误区是以为能用 ClassLoader.getSystemClassLoader().getParent().getParent() 拿到它——实际上 .getParent() 链在 Bootstrap 层就断了,再调用会得到 null,不是抛异常。
- 它不参与双亲委派链的“Java 层调用”,但它是整个委派机制的起点
- 无法通过
new或反射创建,JVM 启动时硬编码绑定 - 如果手动把
rt.jar(或模块化后的java.base)里的类复制到 classpath,JVM 仍优先走 Bootstrap 加载,不会交由 AppClassLoader 处理
Extension ClassLoader 和 AppClassLoader 的路径与替换风险
ExtClassLoader(现在更常称 PlatformClassLoader,JDK 9+)默认加载 $JAVA_HOME/jre/lib/ext(或 java.ext.dirs 指定路径)下的 JAR;AppClassLoader(即 SystemClassLoader)加载 -classpath 或 CLASSPATH 环境变量指定的路径。
注意 JDK 9 引入模块系统后,ExtClassLoader 已被标记为过时,PlatformClassLoader 负责加载平台模块(如 java.xml),但它的父加载器仍是 null(逻辑上等价于 Bootstrap)。
立即学习“Java免费学习笔记(深入)”;
- 不要往
lib/ext放业务 JAR:它会变成“准系统级”类,可能被所有应用共享,引发版本冲突 - 用
-Xbootclasspath/a:追加的路径,会被 Bootstrap 加载器处理,绕过双亲委派检查,极易破坏类型一致性 -
AppClassLoader的加载路径可在运行时通过System.getProperty("java.class.path")查看,但修改该属性不影响已启动的类加载行为
自定义 ClassLoader 必须重写 findClass(),而不是 loadClass()
直接重写 loadClass() 容易破坏双亲委派模型——比如忘了调用 super.loadClass(name),会导致 java.lang.* 类无法加载,JVM 直接崩溃。
正确做法是继承 ClassLoader,只重写 findClass(String name),在里面实现自己的字节码获取逻辑(如从网络、加密文件、数据库读取),然后调用 defineClass() 转成 Class 对象。JVM 会保证先走父加载器,失败后再进 findClass()。
public class MyClassLoader extends ClassLoader {
private final byte[] bytecode;
public MyClassLoader(byte[] bytecode) {
this.bytecode = bytecode;
}
@Override
protected Class> findClass(String name) throws ClassNotFoundException {
try {
return defineClass(name, bytecode, 0, bytecode.length);
} catch (Throwable t) {
throw new ClassNotFoundException(name, t);
}
}
}
- 不要在
findClass()里调用loadClass(),会引发无限递归 -
defineClass()会校验字节码格式和签名,若校验失败抛ClassFormatError,不是ClassNotFoundException - 同一个
ClassLoader实例多次加载同一类名,会抛LinkageError: duplicate class definition
类隔离场景下,ClassLoader 的实例边界比 package 更关键
Web 容器(如 Tomcat)或 OSGi 中的模块隔离,本质靠不同 ClassLoader 实例实现。即使两个类全限定名完全一样(如 com.example.Service),只要由不同加载器加载,JVM 就视为完全不同的类型,不能强转、不能共用静态变量、不能作为方法参数互相传递。
典型错误是把某个类的实例缓存到静态字段里,却没意识到该类可能被多个加载器加载——结果 A 模块的 Service 和 B 模块的 Service 在运行期是“同名异类”。
- 判断类是否相同,得看
class1 == class2,而不是class1.getName().equals(class2.getName()) - 跨加载器通信必须通过接口(且接口由父加载器提供),否则编译期通过、运行期
ClassCastException - 线程上下文类加载器(
Thread.currentThread().getContextClassLoader())常被用来打破双亲委派,但需明确谁设置、何时重置,否则泄漏导致内存问题










