Java 字符串拼接用 +、StringBuilder 还是 StringBuffer?一篇给你终极答案

在 Java 开发中,字符串拼接几乎无处不在。

但是很多人依然搞不清:

  • "a" + "b"StringBuilder.append() 性能差多少?
  • StringBufferStringBuilder 到底该选哪个?
  • 为什么有时 + 也能很快?

本文将从原理、性能、线程安全、最佳实践四个方面,帮你彻底搞懂这个问题。


1. 三者的基本认识

1.1 + 运算符

  • 是 Java 的语法糖
  • 编译期javac 会将非循环内的字符串常量拼接优化为一个常量。
  • 运行期+ 会被编译器转成使用 StringBuilder (早期版本是 StringBuffer,JDK5 后改成 StringBuilder)。
  • 本质上 + 并不是直接的"字符串相加",而是 创建 StringBuilder → append → toString

1.2 StringBuilder

  • 可变 的字符序列(String 是不可变的)。
  • 非线程安全,但速度快。
  • 适用于单线程、多次拼接的场景。
  • API:append()insert()delete() 等。

1.3 StringBuffer

  • StringBuilder 类似,但线程安全 (方法上有 synchronized)。
  • 在多线程并发修改同一字符串时安全,但性能比 StringBuilder 略低。
  • 单线程下不推荐使用。

2. 工作原理 & 编译器优化

2.1 常量折叠(编译期优化)

java 复制代码
String s = "a" + "b" + "c";
System.out.println(s); // abc

编译后字节码中会直接变成:

java 复制代码
String s = "abc";

没有运行期开销


2.2 循环内拼接

java 复制代码
String s = "";
for (int i = 0; i < 1000; i++) {
    s = s + i;
}

编译器会转成:

java 复制代码
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(s).append(i);
    s = sb.toString();
}
  • 每次 toString() 都会创建新 String 对象 → 大量内存浪费,性能差

2.3 手动 StringBuilder

java 复制代码
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
    sb.append(i);
}
String s = sb.toString(); // 一次性生成
  • 只有一次 toString() 调用,性能更优。

3. 性能对比

一个简单的 10000 次拼接测试(单线程):

方法 时间 (ms) 特点
+ (循环内) 400+ 大量中间对象
StringBuilder 3~5 性能最佳
StringBuffer 8~10 线程安全,稍慢

(测试环境:JDK 17,本地单线程,结果随机器差异)


4. 线程安全问题

场景 选择
单线程 StringBuilder(性能优先)
多线程,同一实例被多个线程修改 StringBuffer(保证同步)
多线程,但每个线程有自己实例 StringBuilder(无共享状态)

5. 最佳实践建议

  1. 能用常量直接拼接就用常量(编译期优化,无运行时开销)。

  2. 循环中多次拼接 → 直接用 StringBuilder

  3. 多线程共享可变字符串 → 用 StringBuffer

  4. 拼接规模已知 → 初始化 StringBuilder 容量:

    java 复制代码
    StringBuilder sb = new StringBuilder(1024);

    避免频繁扩容。


6. 小结对比表

特性 + StringBuilder StringBuffer
语法简洁
性能(循环内) 中等
线程安全
适用场景 少量拼接 单线程多拼接 多线程共享

一句话记忆

少量拼接用 +,单线程多次拼接用 StringBuilder,多线程共享用 StringBuffer

相关推荐
B1118521Y464 小时前
flask的使用
后端·python·flask
only-qi4 小时前
146. LRU 缓存
java·算法·缓存
xuxie135 小时前
SpringBoot文件下载(多文件以zip形式,单文件格式不变)
java·spring boot·后端
重生成为编程大王6 小时前
Java中的多态有什么用?
java·后端
666和7776 小时前
Struts2 工作总结
java·数据库
中草药z6 小时前
【Stream API】高效简化集合处理
java·前端·javascript·stream·parallelstream·并行流
野犬寒鸦6 小时前
力扣hot100:搜索二维矩阵 II(常见误区与高效解法详解)(240)
java·数据结构·算法·leetcode·面试
zru_96026 小时前
centos 系统如何安装open jdk 8
java·linux·centos
LiRuiJie7 小时前
深入剖析Spring Boot / Spring 应用中可自定义的扩展点
java·spring boot·spring