string适合存储不可变常量,stringbuilder适合高频拼接;循环中用+拼接会创建大量对象导致gc压力,应预估容量初始化stringbuilder并调用tostring()获取结果。

Java字符串操作看似简单,但不当使用会显著拖慢程序——尤其在循环拼接、日志生成、模板渲染等高频场景。核心问题不在“会不会用”,而在于“什么时候该换工具”。String适合存常量,StringBuilder适合建内容,二者分工明确,混用反而埋坑。
String不是用来拼的,而是用来存的
String对象一旦创建就不能修改,每次“+”或concat()都会生成新对象。比如循环1000次拼接数字,用String会产生1000个中间字符串,全进堆内存,GC压力陡增。它真正的优势是安全和复用:作为Map键、配置项、HTTP头值都极稳妥,因为不可变+常量池机制能省空间、保线程安全。
- 字面量如"hello"自动入常量池,重复声明不新增对象
- new String("hello")一定在堆新建对象,除非显式调用intern()
- 避免在for循环里写result += item,这是性能雷区
StringBuilder是拼接的主力,但得用对姿势
它内部是可扩容的char(或byte)数组,默认容量16。append()只是往数组末尾写,不创建新对象。性能提升关键不在“用了没”,而在“怎么初始化”。没预估长度就直接new StringBuilder(),遇到大数据量时频繁扩容(2倍增长),反而增加数组复制开销。
- 知道最终长度?直接new StringBuilder(预估长度),比如拼JSON字段名+值,可按字符数粗略估算
- 不确定长度但有上限?设个合理初始值,比如new StringBuilder(128)比默认16更稳
- 链式调用没问题:sb.append("a").append(b).append("/").append(c),比拆成多行更简洁
别把StringBuilder当String使
StringBuilder不是String的替代品,而是协作伙伴。它不能直接当参数传给需要String的方法,也不能放进Set或Map当key——因为它可变、没重写equals/hashCode。常见错误是拼完忘了调toString(),结果传了个StringBuilder实例,运行时报ClassCastException或逻辑错乱。
- 拼接结束必须调sb.toString()获取最终String
- 不要把StringBuilder对象长期持有或共享,它非线程安全
- 多线程环境别硬加synchronized——改用StringBuffer,或干脆每个线程用独立StringBuilder
编译器优化有边界,别依赖它
单条语句如String s = "a" + "b" + "c",编译器会在字节码里直接合成常量,跟写"abc"一样快。但这只适用于编译期确定的字面量。一旦涉及变量、方法调用或循环,编译器就无能为力,+号拼接仍会退化成低效的String.concat链。
- 静态拼接放心用+,比如日志前缀"User[" + id + "] logged in"中id是变量,整条仍需StringBuilder
- IDEA或JProfiler能标出潜在拼接热点,建议定期扫描
- JDK9+用String.join()处理集合拼接,比手写循环+StringBuilder更清晰
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











