在 Java 中,字符串拼接主要有以下几种方式,每种方式都有其特定的使用场景和性能特点。

运算符 (最常见,但需注意性能)
这是最直观、最常用的字符串拼接方式,尤其是在 Java 5 之前。
工作原理
在 Java 中, 运算符可以用于连接两个字符串,当你将一个字符串与其他类型(如数字、对象、字符等)使用 连接时,Java 会自动将非字符串类型调用 toString() 方法转换为字符串,然后再进行拼接。
示例代码:
String s1 = "Hello"; String s2 = "World"; String result1 = s1 + " " + s2; // "Hello World" int num = 100; String result2 = "The number is: " + num; // "The number is: 100"
重要性能考量 (在循环中)
运算符在循环中进行字符串拼接时,性能非常差,这是因为 字符串在 Java 中是不可变的(immutable)。

每次使用 拼接字符串时,JVM 都会:
- 在内存中创建一个新的字符串对象。
- 将前一个字符串的内容复制到这个新对象中。
- 再将要拼接的内容追加到新对象中。
- 最后让引用指向这个新创建的对象。
这个过程在循环中会重复成千上万次,导致大量的内存分配和复制操作,造成严重的性能开销和内存浪费。
反例:
// 性能极差的写法!
String result = "";
for (int i = 0; i < 1000; i++) {
result = result + "a"; // 每次循环都创建一个新的 String 对象
}
运算符的特殊情况:编译器优化
从 Java 5 开始,JVM 对 运算符进行了一定的优化,如果编译器在编译时就能确定整个表达式的内容,并且所有参与拼接的元素都是常量,那么它会直接在编译阶段完成拼接,生成一个最终的字符串常量。

示例:
String str = "a" + "b" + "c"; // 编译后等同于 String str = "abc";
这种情况下,不会产生中间的 String 对象,性能很好,只要表达式中有变量,这个优化就不会生效。
StringBuilder 和 StringBuffer (推荐用于循环)
为了解决 运算符在循环中的性能问题,Java 提供了 StringBuilder 和 StringBuffer 这两个类,它们都是 可变字符序列,专门用于高效的字符串拼接操作。
StringBuilder
- 特点:非线程安全,性能高。
- 适用场景:单线程环境下的字符串拼接,例如在方法内部、循环中构建字符串,这是 最常用 的选择。
StringBuffer
- 特点:线程安全,性能稍低(因为方法大多有
synchronized同步锁)。 - 适用场景:多线程环境下,如果多个线程可能同时修改同一个字符串缓冲区,才需要使用
StringBuffer。
核心方法:
append(): 追加内容。toString(): 将构建好的字符序列转换为最终的字符串。
示例代码 (高效写法):
// 推荐:使用 StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append("a"); // 直接在原有对象上追加,不创建新对象
}
String result = sb.toString(); // 最终生成一个 String 对象
这种方式在循环中只创建了一个 StringBuilder 对象,极大地提高了性能和内存效率。
String.concat() 方法
String 类本身也提供了一个 concat() 方法用于拼接。
- 特点:和 运算符类似,它也是 不可变 的,每次调用
concat()都会返回一个新的字符串对象,而原始字符串保持不变。 - 性能:同样不适用于循环中的大量拼接。
示例代码:
String s1 = "Hello";
String s2 = s1.concat(" World"); // 返回一个新的字符串 "Hello World",s1 仍然是 "Hello"
在日常开发中,除非有特殊需求,否则通常优先选择 或 StringBuilder。
String.join() (Java 8+)
如果你需要用一个特定的分隔符来连接一个字符串数组或集合,String.join() 是最方便的选择。
- 特点:代码简洁,可读性高,专门用于处理带分隔符的拼接。
示例代码:
String[] words = {"Java", "is", "awesome"};
String sentence = String.join(" ", words); // "Java is awesome"
List<String> parts = List.of("This", "is", "a", "test");
String anotherSentence = String.join("-", parts); // "This-is-a-test"
String.format() 和 printf
当你需要将变量插入到字符串的特定位置时,类似于 C 语言的 printf,可以使用 String.format()。
- 特点:适合需要格式化输出的场景,如日志、报表生成等。
示例代码:
String name = "Alice";
int age = 30;
String formattedString = String.format("My name is %s and I am %d years old.", name, age);
// "My name is Alice and I am 30 years old."
总结与最佳实践
| 方法 | 线程安全 | 性能 | 适用场景 | 备注 |
|---|---|---|---|---|
| 运算符 | 不适用 | 少量拼接快,循环中极慢 | 少量、简单的字符串拼接,如 String s = "a" + "b"; |
编译器会优化常量拼接,但变量拼接性能差。 |
StringBuilder |
否 | 高 | 循环中、大量字符串拼接 | 首选,性能最佳,单线程下使用。 |
StringBuffer |
是 | 较高 (比 StringBuilder 慢) |
多线程环境下拼接 | 因同步开销,性能略逊于 StringBuilder。 |
String.concat() |
不适用 | 较差 | 少量字符串拼接 | 功能与 类似,不推荐用于复杂场景。 |
String.join() |
不适用 | 高 | 用分隔符连接数组或集合 | Java 8+,代码简洁。 |
String.format() |
不适用 | 较低 | 格式化字符串输出 | 适合日志、模板等场景。 |
核心建议:
-
少量拼接:如果只是拼接几个字符串,直接使用 运算符即可,代码最简洁。
String url = "http://example.com/" + userId + "/profile";
-
循环/大量拼接:务必使用
StringBuilder,这是性能最优的选择。// 在循环外创建 StringBuilder StringBuilder sb = new StringBuilder(); for (...) { sb.append(...); } String finalString = sb.toString(); -
带分隔符的拼接:如果有一组字符串需要用同一个分隔符连接,使用
String.join()。String csvLine = String.join(",", data1, data2, data3); -
格式化输出:如果需要生成格式化的文本(如日志),使用
String.format()。
