String不可变性是类加载机制安全可信执行的基石:类名作为关键元数据全程恒定,支撑双亲委派、权限检查、模块解析、字节码验证、签名信任链及常量池复用,确保JVM语义确定与逻辑一致。

Java 中 String 的不可变性,是类加载器(ClassLoader)实现安全可信执行的底层基石。它不是一种“附加保护”,而是整个类加载机制能正常运转、不被绕过的前提条件。
类名作为关键元数据,必须全程恒定
类加载器依据全限定类名(如 "java.lang.String" 或 "com.example.UserService")定位并加载对应的 .class 文件。这个字符串从调用方传入、经双亲委派链逐级传递、到最终由 defineClass 解析字节码,全程都依赖其内容不变。
- 如果 String 可变,攻击者可能在委派过程中通过反射修改类名引用指向的字符数组,比如把 "javax.crypto.Cipher" 改成 "evil.HackedCipher"
- SecurityManager 做包权限检查(如 "package.access=java.lang")时,也是基于字符串比对;类名若被篡改,权限策略就形同虚设
- 模块系统(Module System)解析 requires、exports 语句,同样以 String 为输入;任意改动都会导致模块解析失败或加载错误模块
不可变性支撑字节码验证与签名信任链
JVM 在加载类前会执行字节码验证,其中一项是检查类的签名、父类名、接口名等——这些全部以 String 形式存在。签名验证(如 JAR 签名)也依赖类名字符串未被中途替换,否则攻击者可将合法类名映射到恶意字节码,绕过签名校验。
- 例如:一个已签名的 "org.apache.commons.codec.binary.Base64" 类,若其类名字符串对象被修改,JVM 可能误认为它是未签名的新类而跳过校验
- 类加载过程中的每一个环节(findBootstrapClass、loadClass、defineClass)都把 String 当作只读元数据使用;不可变性让 JVM 不需要做防御性拷贝,也不必担心跨线程状态污染
常量池复用强化了类名一致性
编译期生成的类名字面量(如 "java.util.ArrayList")会被放入字符串常量池。由于不可变,所有使用该字面量的位置(不同类、不同线程)都指向同一份内存数据。
立即学习“Java免费学习笔记(深入)”;
- 这保证了同一个类名在 JVM 内部始终代表唯一含义,避免因多份副本被分别篡改而导致逻辑分裂
- 类加载器内部缓存(如 ConcurrentHashMap
>)也依赖 key 的哈希值和 equals 行为稳定;String 不可变,才使得 key 永远不会“失联”
本质上,类加载器不是在“防范”某个具体篡改动作,而是在构建一套语义确定的运行环境——String 不可变,就是这个环境最基础的契约。


















