new classloader() 不能加载 java.lang.string,因为核心类由 bootstrapclassloader 强制加载,自定义类加载器双亲委派后仍无法覆盖,且 jvm 会校验包名并拒绝加载 java.* 类。

为什么 new ClassLoader() 不能加载 java.lang.String
因为所有由 JVM 直接识别的核心类(如 java.lang.*、java.util.*)都由 BootstrapClassLoader 加载,它用 C++ 实现、没有 Java 对象引用,也不在类加载器链中。你写的自定义 ClassLoader 子类哪怕重写了 loadClass(),默认也会先委托给父加载器——最终走到 BootstrapClassLoader;但它不认你的字节码,也不会把控制权交回来。
常见错误现象:NoClassDefFoundError 或 ClassNotFoundException 明明 class 文件存在,却报找不到 String、Object 等基础类。
- 不要试图用自定义类加载器去“覆盖”或“替换”核心类——JVM 会直接拒绝加载
- 若需隔离类(比如插件系统),应确保插件 jar 不含
java.*或javax.*包,否则启动时就失败 - 想绕过双亲委派加载自己的
java.util.ArrayList?不行。JVM 在加载阶段会校验包名,java.*包强制走 Bootstrap
ExtensionClassLoader 的路径在哪、现在还用不用
它的标准扩展目录是 $JAVA_HOME/jre/lib/ext(Java 8 及以前),但 Java 9+ 已移除 ExtensionClassLoader,相关机制被模块系统(java.base 等命名模块)取代。如果你在 JDK 11+ 上打印 ClassLoader.getSystemClassLoader().getParent().getParent(),结果是 null——不是 bug,是设计删除。
使用场景:老项目迁移时遇到 ExtClassLoader 相关日志或代码,要意识到它已失效。
立即学习“Java免费学习笔记(深入)”;
- JDK 8 中可通过
-Djava.ext.dirs=/path/to/ext指定额外扩展目录,但该参数在 JDK 9+ 被废弃并报错 - 替代方案:用
--module-path+--add-modules加载模块,或直接把依赖放到 classpath - 别再往
lib/ext放 jar——现代 JDK 启动会忽略,甚至抛SecurityException
AppClassLoader 加载失败的三个典型原因
这是你写 new Object() 时背后实际工作的类加载器,也是绝大多数 ClassNotFoundException 发生的地方。它只管 -cp 或 CLASSPATH 下的路径,不查 jar 内部嵌套、不递归扫描子目录。
常见错误现象:明明 jar 在 classpath 里,却报找不到某个类;或 IDE 跑得通,命令行 java -jar 就失败。
-
java -cp myapp.jar Main不会自动解压 jar 里的依赖——myapp.jar内部的lib/xxx.jar不会被 AppClassLoader 扫到 - 用
java -jar时,MANIFEST.MF中的Class-Path:条目只支持相对路径,且不支持通配符(*是 JDK 6+ 特性,但仅限于java -cp场景,对-jar无效) - IDE 默认把 Maven 依赖全加进运行 classpath,但打包成 fat-jar 或分发时没处理好依赖路径,就会出现“本地 OK,服务器炸了”
自定义类加载器必须重写 findClass() 而不是 loadClass()
因为 loadClass() 默认实现做了双亲委派逻辑,直接重写它容易破坏委托顺序,导致核心类加载异常或重复加载。正确做法是让 loadClass() 走默认流程,只在 findClass() 里专注“从哪读字节码、怎么转成 Class 对象”。
性能影响:每次 findClass() 都要读文件或网络,没缓存的话会显著拖慢反射调用或动态代理生成。
- 务必在
findClass(String name)中把name转为路径(如com.foo.Bar→com/foo/Bar.class),再读取字节码 - 调用
defineClass()前,检查字节码是否为空或长度为 0,否则抛ClassFormatError - 如果需要热替换,注意
ClassLoader实例和它加载的类对象强绑定,旧类无法卸载,除非整个加载器被回收——而 GC 是否回收取决于是否有强引用
类加载器层级不是树形结构而是链式委托,Bootstrap 和 Extension 在现代 JDK 中已大幅弱化,真正可控、可调试、易出错的,始终是 AppClassLoader 及其子类。别花时间猜哪个加载器该加载什么,先看 getClass().getClassLoader() 输出谁,再顺着它的 getParent() 往上捋——大部分问题,卡在第三层就结束了。








