java中string不可变性应顺应而非对抗,高频拼接用stringbuilder(预估容量)、避免+;substring在jdk7u6后独立拷贝,大字符串切片可用charbuffer.wrap;字面量复用常量池,慎用intern;hashcode缓存提升哈希操作效率。

Java 中 String 的不可变性不是要“对抗”,而是要顺应它的设计逻辑——不试图修改它,而是用对的工具做对的事。真正影响性能的,从来不是“String 不可变”这个事实,而是你在需要动态构建字符串时,还坚持用 String 做拼接、截取或替换。
高频拼接:别用 +,改用 StringBuilder
循环内写 s += "x" 看似简洁,实则每次都在创建新 String 和临时 StringBuilder。10 万次操作可能生成 10 万个中间对象。
- 单线程场景下,统一用 StringBuilder:提前预估容量(如 new StringBuilder(512)),避免内部 char[]/byte[] 多次扩容
- 日志组装、JSON 生成、SQL 批量拼接等典型场景,改用 sb.append("User[").append(id).append("]"),实测比链式 + 快 5–10 倍
- 多线程共写同一实例才用 StringBuffer;日常开发中几乎不需要
截取与切片:理解 substring 的行为变迁
JDK 7u6 之后 substring 已改为独立拷贝字符数据,不再共享原字符串底层数组。这意味着安全了,但开销也更实在——每次调用都触发一次数组复制。
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
- 若原始字符串极大(如读取的整份日志),又只需提取极短片段,可考虑用 CharBuffer.wrap(str, start, length) 避免拷贝
- 旧代码兼容 JDK 6 时,可用 new String(str.substring(0, 5)) 强制分离引用,防止大数组滞留
- 简单索引访问(如获取首字符)优先用 str.charAt(0),比 substring(0,1) 更轻量
构造与复用:善用字面量和常量池
“不可变”让 JVM 能安全复用相同内容的字符串,但前提是——你得让它复用上。
- 配置项、枚举值、固定响应体等,一律用字面量:String status = "SUCCESS";,而非 new String("SUCCESS")
- 运行期生成的字符串(如数据库查出的用户名),若重复率高且生命周期长,可谨慎调用 str.intern(),但注意元空间溢出风险
- 避免在循环里对相同字符串反复调用 intern(),它带同步锁,反而拖慢性能
哈希与键值:利用缓存,避开无谓开销
String 的 hashCode 缓存是不可变性带来的隐性红利——首次计算后存入私有字段,后续直接返回。这使它作为 HashMap 键时效率极高。
- 若字符串仅作临时拼接(如日志消息),从不用于哈希集合,那它的 hash 字段就是纯冗余内存(4 字节),无需特别处理
- 但若用作 Map 键、Set 元素或 switch 表达式 case 值,就天然受益于该缓存,不必手动优化
- 对比自定义可变类作 key 时需重写 hashCode 和 equals,String 省心又高效
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










