双亲委派模型通过加载顺序和权限隔离防止核心类被篡改:所有java.lang.*类请求均委托启动类加载器加载,其仅信任rt.jar等受信路径;绕过委派将触发SecurityException;类唯一性由“加载器+全限定名”决定,自定义同名类无法与系统类兼容。

双亲委派模型的核心作用之一,就是阻止像 java.lang.String、java.lang.Object 这类核心变量类被用户自定义版本替换或篡改。它不靠代码拦截,而是靠加载顺序和权限隔离实现本质防护。
加载请求永远先“上交”给启动类加载器
当任意类加载器(比如应用类加载器)收到加载 java.lang.String 的请求时,不会自己查找 .class 文件,而是逐级向上委托——先交给扩展类加载器,再交给启动类加载器(Bootstrap)。启动类加载器只认 JAVA_HOME/jre/lib/rt.jar 等受信路径下的类,并且有 JVM 级别保护,拒绝加载非标准来源的 java.* 类。
- 即使你写了一个同名的
public class String放在项目里,它的全限定名也是java.lang.String - 该请求一发出,立刻被 Bootstrap 拦截并加载官方版本,你的类根本不会进入定义阶段
- 如果强行绕过委派(如重写
loadClass()跳过super.loadClass()),JVM 会在defineClass()阶段直接抛出SecurityException,而非静默失败
类的唯一性由“加载器 + 全限定名”共同决定
Java 规定:两个类是否为同一个类,不仅看名字,还要看是哪个类加载器加载的。这意味着,即便你用自定义加载器成功加载了一个 java.lang.String(比如通过非法手段绕过包限制),它也和 JVM 正常使用的 String 不兼容——无法赋值、无法转型、调用 equals() 会报 ClassCastException。
- 例如:
new java.lang.String()创建的对象,类型是 Bootstrap 加载的String - 而你自定义加载的
String是另一个“同名但不同源”的类,二者在运行时完全隔离 - 这种隔离不是靠语法检查,而是类加载器命名空间天然形成的屏障
系统类加载器不参与 java.* 的定义,仅作“转发通道”
应用类加载器(AppClassLoader)和扩展类加载器(ExtClassLoader)本身不具备定义 java.* 类的能力。它们对这类请求的处理逻辑是:只做委派,不尝试查找本地路径;即使本地存在同名类文件,也不会读取字节码,更不会调用 defineClass()。
-
ClassLoader.findSystemClass(String name)是留给子加载器向 Bootstrap 请求系统类的唯一合法接口 - 这个方法内部由 JVM 实现,只响应
java.*、javax.*等白名单包,且不开放字节码注入入口 - 试图用它加载非系统类会返回
null,加载系统类失败则直接抛异常,不留篡改余地
这套机制不是靠信任开发者,而是靠强制分层和运行时校验,把核心类的控制权牢牢锁死在最顶层。只要不主动破坏委派链,恶意类就根本没有落地执行的机会。










