代码评审考察String不可变性,重在验证开发者是否理解其对逻辑正确性、内存行为和线程安全的实际影响,而非仅记忆final/private;常见问题包括误判对象生命周期、破坏常量池复用、引入线程风险及滥用hashCode缓存。

代码评审中考察 String 不可变性,不是为了确认“你背过 final 和 private”,而是看开发者是否真正理解它对逻辑正确性、内存行为和并发安全的实际影响。评审时一旦发现某段代码隐含地“假设 String 可被修改”,往往意味着潜在 bug、性能浪费或线程风险。
看变量赋值是否误判对象生命周期
常见问题:把引用重新赋值当成原对象被修改。
评审点示例:
- 看到
s = s.replace("a", "b")就结束?要继续查——前面的 s 是否还在其他地方被持有?是否误以为旧字符串已被“覆盖”? - 方法返回 String 但调用方没接住(如
user.getName().toUpperCase();),实际创建了新对象却立即丢弃,属于无意义 GC 开销。 - 日志拼接写成
log.info("id=" + id + ", name=" + name),在高并发下频繁触发 StringBuilder 扩容+toString,应优先考虑参数化日志或 StringJoiner。
查字符串操作是否破坏常量池复用意图
当多个模块共用同一语义字符串(如 SQL 模板、HTTP header key、配置项名),不可变性是它们能安全共享的前提。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
立即学习“Java免费学习笔记(深入)”;
- 评审时关注:是否有人用
new String("CONTENT_TYPE")强制绕过常量池? 这会导致本该复用的对象变成堆中冗余实例,增加 GC 压力。 - 是否在敏感路径(如权限校验)中把用户输入直接拼进固定前缀,再做
.intern()?这可能引发字符串池污染或 DoS 风险(JDK 7/8 中 intern 是堆内操作)。
审多线程场景是否滥用可变替代方案
String 天然线程安全,但开发者有时因“觉得它太重”而换用 StringBuilder 或 char[],反而引入同步隐患。
- 看到类成员字段是
StringBuilder,且被多个线程调用 append —— 即使加了 synchronized,也要问:为什么不用 String + 不可变聚合? - 缓存 key 构造用了
sb.append(userId).append("-").append(timestamp).toString(),但 sb 是单例或静态字段?这是典型的非线程安全写法,应改为每次新建或用 ThreadLocal。 - SQL 拼接工具类返回 StringBuilder 而非 String,迫使调用方自行 toString() —— 评审要确认:这个 builder 是否可能被后续意外修改?
验 hashCode 使用是否依赖缓存稳定性
String 的 hash 值只算一次、长期缓存,是它高效作 HashMap key 的根基。一旦代码试图“复用”同一个 String 实例去代表不同逻辑含义,hash 冲突或缓存失效就可能发生。
- 检查 Map 的 key 是否来自同一字符串字面量但语义不同(如
"timeout"既作配置项名又作错误码)——虽然技术上可行,但违反语义隔离原则,评审应建议拆分成专用常量类。 - 若某处手动缓存了 String 的 hash(如
int cachedHash = s.hashCode()),要确认 s 确实不会被反射篡改(生产环境禁用反射改 String 是基本红线)。

















