在 Java 中,向字符串添加内容(即拼接字符串)有多种方法,选择哪种方法取决于你的具体需求,例如性能、代码可读性以及是否在循环中拼接等。

下面我将详细介绍几种最常用和最重要的方法,并附上代码示例和性能分析。
使用 或 运算符 (最简单,但需注意性能)
这是最直观、最简单的方法,适用于少量字符串的拼接,尤其是在 System.out.println 中。
特点:
- 语法简单:直接使用 连接字符串。
- 底层原理:在 Java 中,使用 拼接字符串时,JVM 实际上会调用
StringBuilder的append()方法来完成。但是,在循环中重复使用 会创建多个StringBuilder对象,导致性能下降。 - 不可变性:
String对象是不可变的,每次拼接操作,都会创建一个新的String对象来存储结果,而不是修改原来的对象。
示例代码:
public class StringConcatenation {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = " ";
String s3 = "World";
String s4 = "!";
// 使用 + 拼接
String result = s1 + s2 + s3 + s4;
System.out.println(result); // 输出: Hello World!
// 使用 += 拼接
String dynamicString = "Start";
dynamicString += " is";
dynamicString += " fun.";
System.out.println(dynamicString); // 输出: Start is fun.
}
}
适用场景:
- 少量字符串的拼接。
- 在
System.out.println等非性能关键路径上使用。 - 代码可读性要求极高,且性能影响微不足道的场景。
使用 StringBuilder (推荐,性能最佳)
StringBuilder 是 Java 5 引入的,专门用于高效地构建字符串,它是一个可变的字符序列,所有修改操作都在同一个对象上进行,避免了创建多个中间字符串对象的开销。
特点:
- 高性能:在循环中拼接字符串时,性能远超 运算符。
- 可变性:
StringBuilder对象是可变的,append()方法会修改对象本身,而不是创建新对象。 - 线程不安全:它的方法不是同步的,因此在多线程环境下需要使用
StringBuffer。
示例代码:
public class StringBuilderExample {
public static void main(String[] args) {
String s1 = "Hello";
String s2 = " ";
String s3 = "World";
String s4 = "!";
// 创建 StringBuilder 对象
StringBuilder sb = new StringBuilder();
// 使用 append() 方法添加内容
sb.append(s1);
sb.append(s2);
sb.append(s3);
sb.append(s4);
// 使用 toString() 方法将 StringBuilder 转换为 String
String result = sb.toString();
System.out.println(result); // 输出: Hello World!
// 链式调用 (更优雅)
String result2 = new StringBuilder()
.append("Java ")
.append("is ")
.append("powerful!")
.toString();
System.out.println(result2); // 输出: Java is powerful!
}
}
适用场景:
- 任何循环中拼接字符串(这是最重要的使用场景)。
- 需要高性能进行大量字符串拼接操作的场景。
- 代码中需要频繁修改字符串内容的场景。
使用 StringBuffer (线程安全的 StringBuilder)
StringBuffer 是 Java 1.0 就存在的类,它与 StringBuilder 功能完全相同,唯一的区别是 StringBuffer 的所有方法都是同步的(synchronized),因此是线程安全的。
特点:
- 线程安全:在多线程环境下,如果多个线程同时修改同一个
StringBuffer对象,是安全的。 - 性能较低:因为同步会带来额外的开销,所以它的性能通常比
StringBuilder稍慢。
示例代码:
public class StringBufferExample {
public static void main(String[] args) {
StringBuffer sb = new StringBuffer();
sb.append("Hello");
sb.append(" ");
sb.append("Thread-safe");
sb.append("!");
String result = sb.toString();
System.out.println(result); // 输出: Hello Thread-safe!
}
}
适用场景:
- 在多线程环境中需要安全地修改字符串。
- 在绝大多数单线程应用中,
StringBuilder是更好的选择,因为它更快。
使用 String.join() (连接字符串数组/列表)
如果你有一个字符串数组、列表或可迭代对象,并且想用特定的分隔符将它们连接起来,String.join() 是最简洁、最优雅的方法。
特点:
- 语法简洁:一行代码即可完成复杂的连接操作。
- 功能明确:专门用于连接多个字符串。
示例代码:
import java.util.Arrays;
import java.util.List;
public class StringJoinExample {
public static void main(String[] args) {
// 连接字符串数组
String[] fruits = {"Apple", "Banana", "Orange"};
String fruitString = String.join(", ", fruits);
System.out.println(fruitString); // 输出: Apple, Banana, Orange
// 连接字符串列表
List<String> colors = Arrays.asList("Red", "Green", "Blue");
String colorString = String.join(" - ", colors);
System.out.println(colorString); // 输出: Red - Green - Blue
// 连接可变参数
String result = String.join(" & ", "Coffee", "Tea", "Milk");
System.out.println(result); // 输出: Coffee & Tea & Milk
}
}
适用场景:
- 需要将一个字符串数组或列表用分隔符连接成一个字符串的场景。
性能对比 (循环场景)
让我们在一个循环中拼接 10,000 个字符串,来直观地比较不同方法的性能差异。
public class PerformanceComparison {
public static void main(String[] args) {
int iterations = 10_000;
String toAdd = "a";
// 1. 使用 + 运算符 (性能极差)
long startTime = System.nanoTime();
String s1 = "";
for (int i = 0; i < iterations; i++) {
s1 += toAdd; // 每次循环都创建新的 StringBuilder 和 String 对象
}
long duration1 = System.nanoTime() - startTime;
System.out.println("Using +: " + duration1 / 1_000_000 + " ms");
// 2. 使用 StringBuilder (性能最佳)
startTime = System.nanoTime();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < iterations; i++) {
sb.append(toAdd); // 在同一个对象上操作
}
String s2 = sb.toString();
long duration2 = System.nanoTime() - startTime;
System.out.println("Using StringBuilder: " + duration2 / 1_000_000 + " ms");
// 3. 使用 StringBuffer (性能略差于 StringBuilder)
startTime = System.nanoTime();
StringBuffer sbuf = new StringBuffer();
for (int i = 0; i < iterations; i++) {
sbuf.append(toAdd);
}
String s3 = sbuf.toString();
long duration3 = System.nanoTime() - startTime;
System.out.println("Using StringBuffer: " + duration3 / 1_000_000 + " ms");
}
}
可能的输出结果 (取决于你的机器):
Using +: 250 ms
Using StringBuilder: 1 ms
Using StringBuffer: 2 ms
从结果可以看出,在循环中, 运算符的性能比 StringBuilder 慢了几个数量级。在循环中拼接字符串,必须使用 StringBuilder。
总结与最佳实践
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| / | 语法简单,直观 | 循环中性能极差,创建多个对象 | 少量拼接,println 调用 |
StringBuilder |
性能最佳,线程不安全 | 需要显式创建对象和调用 toString() |
所有循环拼接、大量拼接 |
StringBuffer |
线程安全 | 性能比 StringBuilder 稍慢 |
多线程环境下的字符串拼接 |
String.join() |
语法简洁优雅 | 仅适用于连接数组/列表 | 连接一个集合中的字符串 |
核心建议:
- 优先使用
StringBuilder:除非你有特殊理由,否则在进行字符串拼接时,StringBuilder是你的首选。 - 避免在循环中使用 :这是一个非常常见的性能陷阱,如果你看到代码中在循环里用 拼接字符串,应该立即将其重构为使用
StringBuilder。 - 只在需要时才用
StringBuffer:如果你的字符串构建逻辑确实涉及多线程共享,才考虑使用StringBuffer,否则,StringBuilder更快。 - 善用
String.join():当你需要连接一个字符串集合时,String.join()是最清晰、最现代的方式。
