【Java】字符串拼接完全指南:从原理到实战优化

大家好,我是云扬~ 字符串拼接是 Java 开发中最常用的操作之一,但很多同学可能没意识到,不同拼接方式的性能差异和底层实现天差地别。今天就带大家深入拆解 5 种常用的字符串拼接方法,结合源码和实战场景,帮你避开坑点、选对方案!

一、+ 号操作符:语法糖的甜蜜与陷阱

我们最熟悉的+号拼接,本质是 Java 编译器提供的语法糖。先看一个直观示例:

// 原代码
String a = "Hello";
String b = "World";
String c = a + b;

// 编译后实际执行(反编译class文件)
String a = "Hello";
String b = "World";
String c = new StringBuilder().append(a).append(b).toString();

关键特性:

  1. 编译期优化:如果拼接的是编译时常量,会直接优化为常量字符串:javaString d = 11 + ""; // 编译后直接是 String d = "11";
  2. 循环中的致命问题:循环体内使用+号会创建大量StringBuilder对象,导致内存浪费和 GC 频繁:
// 反面示例:循环中滥用+号(性能极差)
String result = "";
for (int i = 0; i<1000; i++) {
    result += i; // 每次循环都新建StringBuilder
}

二、StringBuilder.append:循环拼接的最优解

既然+号在循环中会创建多个StringBuilder,那直接手动控制StringBuilder实例即可:

正确写法:

// 正面示例:循环外创建StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i< 1000; i++) {
    sb.append(i); // 复用同一个对象,性能拉满
}
String result = sb.toString();

源码核心逻辑(AbstractStringBuilder):

// 简化核心流程
public AbstractStringBuilder append(String str) {
    if (str == null) return appendNull(); // null处理为"null"
    int len = str.length();
    ensureCapacityInternal(count + len); // 数组扩容检查
    str.getChars(0, len, value, count); // 字符数组复制
    count += len;
    return this;
}

关键优化点:

  • 初始容量:默认 16 字符,可指定初始容量减少扩容次数:new StringBuilder(1024)
  • 线程不安全:单线程场景使用,多线程用StringBuffer(效率较低)

三、String.concat:简单拼接的轻量选择

concat()方法是 String 类原生的拼接方式,语法简洁:

使用示例:

String a = "Hello";
String b = "World";
String c = a.concat(b); // 结果:"HelloWorld"

源码核心特性:

public String concat(String str) {
    int otherLen = str.length();
    if (otherLen == 0) {
        return this;
    }
    int len = value.length;
    char buf[] = Arrays.copyOf(value, len + otherLen);
    str.getChars(buf, len);
    return new String(buf, true);
}
  1. 空字符串优化:拼接空字符串时直接返回原对象,无需创建新字符串
  2. null 处理:遇到 null 会抛出NullPointerException(与+号的 “null” 处理不同):
// 对比测试
String e = null + "test"; // 结果:"nulltest"
String f = "test".concat(null); // 抛出NPE

3. 适用场景:仅需拼接 1-2 个字符串,且确定无 null 的场景

四、String.join:带分隔符的优雅拼接

Java 8 新增的String.join()静态方法,专为多字符串拼接 + 分隔符设计:

基本用法:

// 分隔符 + 可变参数
String names = String.join(",", "张三", "李四", "王五");
System.out.println(names); // 输出:"张三,李四,王五"

// 分隔符 + 集合
List<String> fruits = Arrays.asList("苹果", "香蕉", "橙子");
String fruitStr = String.join("|", fruits);
System.out.println(fruitStr); // 输出:"苹果|香蕉|橙子"

底层实现:

public static String join(CharSequence delimiter, CharSequence... elements) {
    Objects.requireNonNull(delimiter);
    Objects.requireNonNull(elements);
    // Number of elements not likely worth Arrays.stream overhead.
    StringJoiner joiner = new StringJoiner(delimiter);
    for (CharSequence cs: elements) {
        joiner.add(cs);
    }
    return joiner.toString();
}

内部通过StringJoiner实现,自动处理分隔符,避免手动拼接分隔符的冗余代码。

五、StringUtils.join:空安全的实用工具

来自 org.apache.commons.lang3StringUtils.join(),解决了 null 处理的痛点:

核心优势:

// 自动忽略null,无需手动判空
String[] arr = {"Java", null, "Python", "Go"};
String result = StringUtils.join(arr, "-");
System.out.println(result); // 输出:"Java-Python-Go"

// 集合拼接同样支持
List<String> list = Arrays.asList("Spring", null, "MyBatis");
String listResult = StringUtils.join(list, ",");

底层逻辑:

public static String join(final Object[] array, String separator, final int startIndex, final int endIndex) {
    if (array == null) {
        return null;
    }
    if (separator == null) {
        separator = EMPTY;
    }

    final StringBuilder buf = new StringBuilder(noOfItems * 16);

    for (int i = startIndex; i < endIndex; i++) {
        if (i > startIndex) {
            buf.append(separator);
        }
        if (array[i] != null) {
            buf.append(array[i]);
        }
    }
    return buf.toString();
}

内部仍使用StringBuilder实现,增加了 null 过滤逻辑,开发效率和性能兼顾。

六、方法对比与场景选择

拼接方式优点缺点适用场景
+ 号操作符语法简洁循环中性能差少量字符串拼接
StringBuilder.append性能最优语法稍繁琐循环拼接、大量字符串
String.concat轻量无额外依赖不支持 null、多字符串拼接麻烦1-2 个非 null 字符串拼接
String.join分隔符优雅处理不支持 null(需手动过滤)带分隔符的多字符串拼接
StringUtils.join空安全、功能全面需引入第三方依赖项目已依赖 Commons Lang3

总结

  1. 日常开发中,循环拼接优先用StringBuilder.append,记得指定初始容量
  2. 少量字符串拼接用+号即可,代码简洁易读
  3. 带分隔符的拼接优先用String.join,干净优雅
  4. 涉及 null 的场景直接用StringUtils.join,避免空指针踩坑

字符串拼接看似简单,实则藏着不少优化细节。选择合适的拼接方式,既能提升代码性能,也能让代码更具可读性。你在项目中常用哪种拼接方式?欢迎在评论区交流~

Tags:

发表回复

Your email address will not be published. Required fields are marked *.

*
*