String类通过私有int字段hash缓存哈希值,首次调用hashCode()时计算并存储,后续直接返回;该机制依赖字符串不可变性保证缓存有效性,且采用乐观无锁设计容忍极小概率的重复计算。

Java 的 String 类在内部使用一个 hash 字段实现哈希缓存,核心目的是避免对同一字符串重复计算哈希值。
hash 字段的定义与初始化
String 类中声明了一个私有、非 volatile 的 int hash 字段,默认值为 0。这个 0 具有双重含义:既表示“尚未计算”,也可能是合法的哈希结果(例如空字符串 "" 的哈希值就是 0)。因此不能仅靠是否为 0 判断是否已缓存。
真正区分的方式是:首次调用 hashCode() 时,若 hash == 0 且字符串非空,则执行计算并赋值;之后直接返回缓存值。
哈希计算逻辑与缓存时机
String.hashCode() 使用标准多项式公式:
h = s[0] × 31(n−1) + s[1] × 31(n−2) + … + s[n−1]
该计算只在 hash 为 0 且字符串长度 > 0 时触发,并将结果写入 hash 字段。一旦写入,后续调用不再重新计算。
注意:该字段不是 final,也不是 volatile,说明设计上接受“多线程下可能重复计算一次”的轻微冗余,以换取无锁读取的高性能。
为什么不用 volatile 或 synchronized?
重复计算哈希值本身开销极小(O(n)但 n 通常很小),且结果确定、无副作用。强制同步会显著拖慢高频调用场景(如大量 HashMap 插入)。
实际中,多个线程可能几乎同时发现 hash == 0 并各自计算——结果一致,最多浪费几纳秒,但避免了锁竞争和内存屏障开销。这是典型的“乐观无锁缓存”设计。
不可变性是缓存安全的前提
因为 String 是不可变的,其字符内容和长度在构造后永不改变。这意味着:
– 缓存的哈希值永远有效
– 无需监听内容变更或失效缓存
– 不会出现“值变了但 hash 还是旧的”这类一致性错误
如果字符串可变,这套缓存机制将完全失效,甚至引发严重 bug。

















