优先用stringbuilder,循环拼接必须用它;+适合单次少量拼接(编译期优化);concat()仅限两个非空字符串且需判null。

字符串拼接用 + 还是 concat()?
在 Java 中,+ 拼接字符串看似最方便,但编译器会根据上下文做不同处理:局部变量拼接(尤其在循环外、常量多)时,JVM 通常会优化成 StringBuilder;但若在循环内反复用 +,每次都会新建 StringBuilder 实例再 toString(),实际等价于手动 new 多次对象。而 concat() 是 String 类的实例方法,仅适用于两个字符串拼接,底层直接复制字节数组,无对象创建开销,但不支持 null ——传入 null 会抛 NullPointerException。
常见错误现象:"a" + null 得到 "anull"(因为 + 被重载为字符串连接,null 自动转为字符串),但 "a".concat(null) 直接崩溃。
- 场景建议:两个非空字符串拼接且已知长度较小时,
concat()略快于+;其他情况别硬套 - 注意
concat()不接受null,而+和StringBuilder.append()都会调用String.valueOf()安全转换
循环内拼接必须用 StringBuilder
这是性能差异最显著的场景。用 + 或 concat() 在 for 循环里拼接 N 次字符串,时间复杂度接近 O(N²),因为每次拼接都要复制前序所有字符。而 StringBuilder 默认初始容量 16,自动扩容策略是翻倍(16 → 32 → 64…),append 操作平均摊还成本为 O(1)。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 预估总长度,构造时指定容量:
new StringBuilder(estimatedTotalLength),避免多次数组复制 - 不要写
sb.append("").append("a").append("b")——空字符串 append 是无效操作,纯属冗余 - 注意线程安全:如果多线程共用同一
StringBuilder实例,需同步;否则优先选它,别用线程安全但更慢的StringBuffer
编译期优化 vs 运行期行为的区别
String a = "x" + "y" + "z"; 这种全是字面量的拼接,javac 在编译阶段就合并为 "xyz",运行时零开销;但只要含一个变量,例如 "x" + s + "z"(s 是运行时才知道的 String),就会生成 StringBuilder 相关字节码。
验证方式:用 javap -c YourClass 查看字节码,你会看到 new StringBuilder、append、toString 调用 —— 这说明所谓“+ 很慢”其实是误解,真正慢的是没意识到它在循环中被重复实例化。
- IDEA 或 Eclipse 的字节码查看插件能直观看到差异
- 不要仅凭源码写法判断性能,要看实际生成的字节码和运行时对象分配
实际项目中该选哪个?
没有银弹,但有明确优先级:
- 单次拼接(≤3 个项)、含常量、无循环:放心用
+,可读性好,编译器帮你优化 - 确定只有两个非空字符串、对微秒级延迟敏感(如高频日志格式化):考虑
concat(),但要加null判断 - 循环拼接、动态构建 SQL/JSON/HTTP Body、不确定长度:必须用
StringBuilder,且记得预设容量
最容易被忽略的一点:很多人把 StringBuilder 当作“高级技巧”,结果在 for 循环里写 result += item,既慢又产生大量临时对象 —— 这类问题在线上服务 GC 日志里常表现为频繁的 young gc 和堆内存抖动。











