在Java开发中,字符串连接是最基础的操作之一,无论是拼接用户输入、组装SQL语句,还是构建动态文本,都离不开字符串连接,掌握不同的连接方法及其适用场景,不仅能提升代码效率,还能避免潜在的性能问题,本文将系统介绍Java中字符串连接的多种方式,分析其原理、优缺点及最佳实践。

基础连接方式:使用运算符
运算符是Java中最直观的字符串连接方式,通过将字符串与其他类型(或字符串)直接相加,实现拼接效果,对于初学者而言,这种方式简单易懂,代码可读性高。
示例代码
String str1 = "Hello"; String str2 = "Java"; String result = str1 + " " + str2 + "!"; // 输出: "Hello Java!"
原理与注意事项
在编译阶段,Java编译器会对运算符进行优化:如果拼接的操作数均为常量(如"a" + "b"),编译器会直接在编译时完成拼接,生成最终的字符串常量;如果包含变量(如str1 + " "),则会在运行时隐式调用StringBuilder的append()方法完成拼接。
运算符在循环中使用时可能存在性能问题。
String result = "";
for (int i = 0; i < 1000; i++) {
result += i; // 每次循环都会创建新的StringBuilder对象
}
由于字符串是不可变的(final),每次操作都会生成一个新的字符串对象,频繁创建和销毁对象会导致性能下降。运算符仅适用于少量字符串拼接的场景,如简单的字符串组装或日志拼接。
传统方法:concat()函数
String.concat()是Java提供的字符串连接方法,用于将指定字符串拼接到当前字符串的末尾,与运算符不同,concat()方法显式调用,语法上更符合“方法调用”的思维。
示例代码
String str1 = "Hello";
String str2 = "Java";
String result = str1.concat(" ").concat(str2).concat("!"); // 输出: "Hello Java!"
原理与优缺点
concat()方法的内部实现是创建一个新的字符数组,将原字符串和待拼接字符串的内容复制到新数组中,再返回新字符串对象,其核心逻辑与运算符类似,但不会自动处理非字符串类型的转换(如concat(123)会编译报错,需手动转换为字符串)。
优点:

- 代码语义明确,适合显式表达“连接”操作;
- 对于少量拼接,性能与运算符接近(编译器优化后差异较小)。
缺点:
- 无法直接拼接非字符串类型,需调用
String.valueOf()转换; - 在循环中同样存在性能问题(每次创建新对象);
- 如果待拼接字符串为
null,会抛出NullPointerException(而StringBuilder.append()会处理null,输出"null"字符串)。
concat()的适用场景与运算符类似,更适合少量、明确的字符串连接,且需确保操作数非空。
高效选择:StringBuilder与StringBuffer
当需要频繁拼接字符串(如循环中、大量数据组装)时,StringBuilder和StringBuffer是更优的选择,两者均采用可变的字符数组,通过动态扩容减少对象创建,显著提升性能。
核心区别
| 特性 | StringBuilder |
StringBuffer |
|---|---|---|
| 线程安全 | 不安全 | 安全 |
| 性能 | 更快 | 稍慢 |
| 适用场景 | 单线程环境 | 多线程环境 |
StringBuffer的方法均使用synchronized修饰,保证了线程安全,但同步开销会降低性能;而StringBuilder去掉同步机制,在单线程下性能更优。实际开发中,除非明确需要线程安全,否则优先使用StringBuilder。
示例代码
// 使用StringBuilder拼接字符串
StringBuilder sb = new StringBuilder();
sb.append("Hello").append(" ").append("Java").append("!");
String result = sb.toString(); // 输出: "Hello Java!"
// 循环中使用StringBuilder(高效)
StringBuilder efficientSb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
efficientSb.append(i);
}
String efficientResult = efficientSb.toString();
常用方法
append():拼接任意类型(自动调用toString()),支持链式调用;toString():将StringBuilder/StringBuffer转换为字符串;insert():在指定位置插入字符串;reverse():反转字符串;setLength():设置字符串长度(截断或填充空字符)。
性能优势
StringBuilder的内部维护一个字符数组,初始容量为16(可通过构造方法指定,如new StringBuilder(100)),当拼接内容超过容量时,会自动扩容(通常为原容量的2倍+2),这种“预分配+动态扩容”机制避免了频繁创建对象,使得循环拼接的性能远超和concat(),拼接1000个字符串时,StringBuilder仅需1次对象创建,而运算符需要创建1000个新对象。
Java 8+新特性:String.join()与Stream拼接
Java 8及以上版本提供了更便捷的字符串连接方式,适合特定场景下的批量拼接。
String.join():使用分隔符连接多个字符串
String.join()方法通过指定分隔符,将数组、集合等中的字符串元素拼接为一个完整字符串。

示例代码
// 连接数组
String[] fruits = {"Apple", "Banana", "Orange"};
String result1 = String.join(", ", fruits); // 输出: "Apple, Banana, Orange"
// 连接集合
List<String> colors = Arrays.asList("Red", "Green", "Blue");
String result2 = String.join(" - ", colors); // 输出: "Red - Green - Blue"
Stream的Collectors.joining():流式拼接
对于复杂的数据处理场景(如过滤、映射后拼接),可以使用Stream API的Collectors.joining()方法,支持自定义分隔符、前缀和后缀。
示例代码
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
String result = names.stream()
.filter(name -> name.length() > 3) // 过滤长度大于3的名字
.map(String::toUpperCase) // 转为大写
.collect(Collectors.joining("|", "[", "]")); // 输出: "[ALICE|BOB|CHARLIE]"
适用场景
String.join():适合简单、快速的数组/集合拼接,分隔符统一;Stream.joining():适合需要先对数据做处理(过滤、转换等)再拼接的场景,灵活性更高。
性能对比与场景选择
为了直观展示不同方法的性能差异,我们通过一个简单测试拼接10000次字符串:
// 测试+运算符
long start1 = System.currentTimeMillis();
String test1 = "";
for (int i = 0; i < 10000; i++) {
test1 += i;
}
long end1 = System.currentTimeMillis();
System.out.println("+运算符耗时: " + (end1 - start1) + "ms");
// 测试StringBuilder
long start2 = System.currentTimeMillis();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) {
sb.append(i);
}
String test2 = sb.toString();
long end2 = System.currentTimeMillis();
System.out.println("StringBuilder耗时: " + (end2 - start2) + "ms");
测试结果(仅供参考):
- 运算符:约100-200ms(取决于JVM优化);
StringBuilder:约1-5ms。
| 场景 | 推荐方法 | 原因 |
|---|---|---|
| 少量拼接(1-3次) | 运算符或concat() |
代码简洁,可读性高 |
| 循环或大量拼接 | StringBuilder |
性能最优,避免对象创建 |
| 多线程环境拼接 | StringBuffer |
保证线程安全 |
| 数组/集合批量拼接 | String.join() |
语法简洁,支持分隔符 |
| 流式数据处理后拼接 | Stream.joining() |
灵活性高,可结合中间操作 |
最佳实践与注意事项
- 优先避免循环中使用运算符:除非确认编译器能优化(如JIT优化后的
StringConcatFactory),否则循环中务必使用StringBuilder。 - 合理设置初始容量:使用
StringBuilder时,若预估拼接字符串长度,可通过new StringBuilder(capacity)减少扩容次数(如new StringBuilder(1000))。 - 注意
null值处理:concat()和运算符遇到null会抛异常,而StringBuilder.append()会将null转为"null"字符串,需根据业务需求选择。 - 格式化输出优先用
String.format():若涉及变量替换和格式控制(如日期、数字格式化),String.format()比拼接更清晰(如String.format("Name: %s, Age: %d", name, age))。
Java字符串连接方法从简单的运算符到高效的StringBuilder,再到Java 8的流式拼接,不断演进以满足不同场景的需求,开发者需根据拼接次数、线程环境、代码可读性等因素选择合适的方法:少量拼接用或concat(),大量拼接用StringBuilder,批量处理用String.join()或Stream.joining(),理解底层原理(如字符串不可变性、对象创建成本)并遵循最佳实践,才能写出高效、健壮的字符串连接代码。
















