根本原因是双亲委派模型使自定义java.lang.String从未被尝试加载:启动类加载器优先加载rt.jar中的原生版本,整个委派链在顶层即成功结束,自定义类根本未进入加载流程。

我们自己写的 java.lang.String 类不会被加载,根本原因不是 JVM “禁止”它,而是双亲委派模型天然切断了它的加载路径——它连被尝试加载的机会都没有。
启动类加载器先“认领”了这个任务
当你写了一个 package java.lang; public class String { ... },JVM 在加载时不会直接交给应用类加载器(AppClassLoader)处理。而是按双亲委派规则,先向上委托:
- 请求先传给应用类加载器 → 它不自己干,转给父加载器(扩展类加载器);
- 扩展类加载器也不干,继续上交 → 最终到达最顶层的启动类加载器(Bootstrap ClassLoader);
- 启动类加载器一看全限定名是 java.lang.String,立刻响应:这个我熟,rt.jar 里早就有,直接加载 JDK 自带的版本。
整个过程没有失败环节,委派链在顶层就成功结束。自定义的 String 根本没被任何加载器“看到”,更谈不上加载或冲突。
即使绕过委派,也大概率会失败
有人尝试重写 loadClass 方法跳过双亲委派,强行用自定义类加载器去加载自己的 java.lang.String。但这样仍会出问题:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
立即学习“Java免费学习笔记(深入)”;
- 启动类加载器已将核心
java.lang.*类加载进方法区,且这些类由 C++ 实现的 Bootstrap 加载,具有最高信任等级; - JVM 对
java.lang包有硬性保护机制:非 Bootstrap 加载的同名类,在链接(Linking)阶段的“验证”环节就会被拒绝,抛出SecurityException或IllegalAccessError; - 即便侥幸通过验证,也会导致严重不一致:比如你写的 String 调用
System.out.println(),而后者依赖的是原始 String,类型系统立即崩溃。
安全性的本质是“信任分层”而非“绝对封锁”
双亲委派的安全性不靠密码或权限表,而靠加载器职责的严格划分:
-
Bootstrap 只加载
java.、javax.等白名单包,且只从rt.jar等可信路径读取; -
ExtClassLoader 加载
lib/ext下的扩展,不碰核心包; - AppClassLoader 只管用户代码,无权覆盖上面两层已加载的类。
这种结构让恶意代码无法“狸猫换太子”——哪怕你把伪造的 String 放进 classpath,它也永远排在启动类加载器的正牌 String 后面,且根本不会被轮到。
所以,不是“不能写”,而是“写了也没用”;不是“加载失败”,而是“压根没轮到你加载”。这才是双亲委派真正牢靠的地方。


















