Java字符串拼接需按场景选择方式:编译期常量用+号最优;循环内必须用StringBuilder避免重复创建;集合转字符串优先String.join。

Java字符串拼接看似简单,但不同写法在字节码层面差异巨大,直接影响运行时性能和GC压力。关键不在“会不会写”,而在于“哪段代码该用哪种方式”——编译器不会替你做所有决定,尤其在动态场景下。
编译期常量拼接:+号最安全
当所有操作数都是字面量或final修饰的编译期常量时,javac会直接执行常量折叠,不生成任何拼接逻辑。
例如:
String msg = "User" + ":" + id;(id为final int)
字节码中只有一条 ldc 指令加载合并后的完整字符串,无对象创建、无方法调用。这种场景下用+不仅简洁,而且最优。
但注意:
- String a = "A"; String b = "B"; String c = a + b; 不触发折叠(a、b是变量,非编译期常量)
- final修饰必须作用于变量声明本身,且初始化值也是常量
循环内拼接:必须用StringBuilder,禁用+=
在for/while循环中使用 str += "x",JDK 8及以前会为每次迭代生成一个StringBuilder实例;JDK 9+虽改用InvokeDynamic,但仍需动态解析策略,无法避免重复构造与toString开销。
反编译可见:
- f1()方法(用+=):循环体内部反复出现 new StringBuilder、append、toString
- f2()方法(显式StringBuilder):仅一次new,后续全是append调用,无toString中间态
建议写法:
- 提前预估容量:new StringBuilder(estimatedLength),避免数组扩容复制
- 避免在循环内调用toString(),直到最后统一调用一次
集合转字符串:优先String.join
对List、Array等已有结构做分隔连接,String.join 是语义最清晰、实现最优化的选择。
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
它内部使用预分配数组+批量拷贝,比手动遍历+StringBuilder更少分支判断,也无需处理首尾分隔符逻辑。
示例:
String result = String.join(", ", list);
比以下写法更高效且不易出错:
sb.append(list.get(0)); for (int i = 1; i
动态拼接场景:关注InvokeDynamic与策略选择
JDK 9+将 a + b + c 编译为单条 invokedynamic 指令,由StringConcatFactory在首次执行时选择具体策略:
- MH_INLINE_SIZED_EXACT:已知所有参数长度,一次性分配精确字节数组
- MH_UNSAFE_*:绕过边界检查,JVM底层优化路径
- fallback到StringBuilder策略(参数含null或复杂表达式时)
这意味着:动态拼接不一定慢,但不可控。若性能敏感(如日志拼接、协议组装),仍推荐显式StringBuilder——你能掌控容量、复用对象、避免首次调用引导开销。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










