jvm 忽略自定义 java.lang.string 是因双亲委派模型中启动类加载器优先加载并缓存该类,后续加载请求直接返回已加载的 class 对象,自定义类根本不会被查找或加载。

不会加载。JVM 会直接忽略你写的 java.lang.String,始终使用 JDK 自带的那个。
为什么自定义的 String 类根本进不了加载流程
不是“加载失败”,而是压根没机会走到你的类加载器逻辑里。关键在双亲委派模型的执行顺序和启动类加载器的绝对优先权:
- 当你写
new String()或引用String时,JVM 立即触发类加载请求,目标是全限定名java.lang.String - 请求首先交给应用类加载器(AppClassLoader),但它不自己处理,而是向上委托给扩展类加载器(ExtClassLoader)
- 扩展类加载器继续向上委托,最终到达启动类加载器(BootstrapClassLoader)
- 启动类加载器负责
rt.jar(或modules中的java.base)里的核心类,它一查就发现java/lang/String.class已存在且已加载,立刻返回该 Class 对象 - 整个过程在委派链顶端就结束了,你的自定义类连被查找的机会都没有
底层拦截的关键点:loadClass 方法里的硬编码逻辑
所有标准类加载器(包括 AppClassLoader、ExtClassLoader)的 loadClass(String, boolean) 方法都遵循同一套模板,核心逻辑如下:
- 先调用
findLoadedClass(name)查缓存——如果已加载,直接返回 - 若未加载,且父加载器非 null,则调用
parent.loadClass(name, resolve)委托 - 只有父加载器抛出
ClassNotFoundException,当前加载器才调用findClass(name)尝试自己加载
而启动类加载器由 JVM 用 C++ 实现,不走 Java 层的 loadClass,它对 java.lang.* 包下的类有专属路径扫描和强绑定。你的 .class 文件即使放在 classpath 里,也不会出现在它的搜索路径中。
就算绕过委派,也过不了验证关
假设你强行重写 loadClass 跳过委托(即打破双亲委派),自己去读取并 define 一个 java.lang.String:
- JVM 启动后会对所有
java.lang.*包下的类做“包级保护”校验 -
defineClass方法内部会检查:若类名以java.开头,且不是由启动类加载器加载的,直接抛SecurityException - 这是 JVM 级别的硬性安全策略,无法通过 Java 代码绕过
可以试试但注定失败的操作
有人尝试把自定义 String 打成 jar 放到 $JAVA_HOME/jre/lib/ext/ 下,指望扩展类加载器加载——同样无效:
- 扩展类加载器仍会向上委托给启动类加载器
- 启动类加载器已命中缓存,不会继续向下查找
- 而且从 Java 9 开始,
ext机制已被模块系统取代,该路径本身也不再参与类加载
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











