一、Java 19 中字符串拼接的核心方式

Java 19 本身并未对字符串拼接的核心机制做颠覆性改动,但结合 JDK 长期演进的优化(如 StringConcatFactoryinvokedynamic 等),以下是主流且高效的拼接方式,按「场景适配性」和「性能」排序:

1. 基础拼接:+ 运算符(编译期优化)

适用场景:少量、固定数量的字符串拼接(如静态文本 + 少量变量)。

原理:Java 编译器会自动优化 + 拼接,对于编译期可确定的拼接(如常量),直接合并为一个字符串;对于运行时变量,会自动替换为 StringBuilder(而非低效的 String 拼接),无需手动写 StringBuilder

示例代码

public class StringConcatExample {
    public static void main(String[] args) {
        String name = "Java 19";
        int version = 19;
        // 编译期优化为 StringBuilder.append(),无需手动优化
        String result = "Hello, " + name + "! Version: " + version;
        System.out.println(result); // 输出:Hello, Java 19! Version: 19
    }
}

注意

  • 仅在「单次拼接」场景下高效;若在循环中使用 +(如 for 循环里 str += i),编译器无法优化(每次循环都会新建 StringBuilder),性能会急剧下降。

2. 高效拼接:StringBuilder(运行时动态拼接)

适用场景:循环拼接、动态数量的字符串拼接(如遍历集合拼接)。

原理StringBuilder 是可变字符序列,底层通过 char 数组存储,避免了 String 不可变导致的频繁对象创建,是 Java 中最通用的高效拼接工具(非线程安全,单线程首选)。

最佳实战示例

import java.util.ArrayList;
import java.util.List;

public class StringBuilderExample {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Java");
        list.add("Python");
        list.add("Go");

        // 初始化时指定容量(避免数组扩容,进一步优化性能)
        StringBuilder sb = new StringBuilder(100); // 预估最终字符串长度
        for (String lang : list) {
            sb.append(lang).append(", ");
        }

        // 处理末尾多余的分隔符(最佳实践)
        if (sb.length() > 0) {
            sb.setLength(sb.length() - 2);
        }

        String result = sb.toString();
        System.out.println(result); // 输出:Java, Python, Go
    }
}

关键技巧

  • 初始化 StringBuilder 时指定预估容量(如 new StringBuilder(100)),避免底层 char 数组频繁扩容(默认初始容量 16,扩容时会复制数组,损耗性能)。

  • 循环拼接时优先用 StringBuilder,而非 +StringBuffer

3. 线程安全拼接:StringBuffer

适用场景:多线程环境下的字符串拼接(极少场景)。

原理StringBufferStringBuilder 的线程安全版本,所有方法加了 synchronized 锁,性能比 StringBuilder 低约 10-20 倍。

示例代码

public class StringBufferExample {
    public static void main(String[] args) throws InterruptedException {
        StringBuffer sb = new StringBuffer();
        // 多线程拼接(仅示例,实际极少用)
        Runnable task = () -> {
            for (int i = 0; i < 100; i++) {
                sb.append("a");
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println(sb.length()); // 输出:200(线程安全)
    }
}

最佳实践

  • 单线程场景绝对优先用 StringBuilder,仅当必须保证线程安全时才用 StringBuffer(实际开发中多线程拼接更推荐「线程局部 StringBuilder + 最后合并」,而非直接用 StringBuffer)。

4. JDK 8+ 便捷拼接:String.join()

适用场景:集合 / 数组元素拼接(带固定分隔符)。

原理:底层基于 StringJoiner(本质还是 StringBuilder),语法简洁,无需手动处理分隔符。

示例代码

import java.util.Arrays;
import java.util.List;

public class StringJoinExample {
    public static void main(String[] args) {
        List<String> fruits = Arrays.asList("Apple", "Banana", "Orange");
        // 分隔符 + 集合,一行完成拼接
        String result = String.join(", ", fruits);
        System.out.println(result); // 输出:Apple, Banana, Orange

        // 也支持可变参数
        String result2 = String.join("-", "Java", "19", "String");
        System.out.println(result2); // 输出:Java-19-String
    }
}

5. JDK 8+ 灵活拼接:StringJoiner

适用场景:需要自定义「分隔符、前缀、后缀」的拼接(如拼接成 JSON 数组、SQL IN 条件)。

示例代码

import java.util.StringJoiner;

public class StringJoinerExample {
    public static void main(String[] args) {
        // 分隔符 + 前缀 + 后缀
        StringJoiner sj = new StringJoiner(", ", "[", "]");
        sj.add("1").add("2").add("3");
        String result = sj.toString();
        System.out.println(result); // 输出:[1, 2, 3]

        // 适配 SQL IN 条件场景
        StringJoiner sqlIn = new StringJoiner("','", "'", "'");
        sqlIn.add("Java").add("Python");
        String sql = "SELECT * FROM lang WHERE name IN (" + sqlIn + ")";
        System.out.println(sql); // 输出:SELECT * FROM lang WHERE name IN ('Java','Python')
    }
}

6. JDK 15+ 文本块拼接:Text Blocks(增强可读性)

适用场景:多行字符串拼接(如 SQL、JSON、HTML 等),Java 15 正式引入,Java 19 完全支持。

示例代码

public class TextBlocksExample {
    public static void main(String[] args) {
        String name = "Java 19";
        int version = 19;
        // 文本块 + 插值(Java 暂无原生插值,需结合 + 或 Formatter)
        String html = """
                <html>
                  <head><title>%s</title></head>
                  <body>Version: %d</body>
                </html>
                """.formatted(name, version);
        System.out.println(html);
        // 输出:
        // <html>
        //   <head><title>Java 19</title></head>
        //   <body>Version: 19</body>
        // </html>
    }
}

7. 格式化拼接:String.format()

适用场景:需要格式化(如数字、日期)的拼接,可读性优先,性能略低于 StringBuilder

%s 字符串、%d 整数、%.2f 保留两位小数

示例代码

# String.format方式
public class StringFormatExample {
    public static void main(String[] args) {
        double price = 99.9;
        String product = "Java 实战";
        // 格式化拼接
        String result = String.format("商品:%s,价格:%.2f 元", product, price);
        System.out.println(result); // 输出:商品:Java 实战,价格:99.90 元
    }
}

# String.format的简化版
String multiLine = """
                姓名:%s
                年龄:%d
                薪资:%.2f 元/月
                """.formatted(name, age, salary);

二、Java 19 字符串拼接最佳实战总结

拼接方式

适用场景

性能

核心技巧

+ 运算符

少量、固定数量的静态 / 动态拼接

编译期自动优化,避免循环中使用

StringBuilder

循环、动态数量拼接(单线程)

最高

初始化指定容量,避免频繁扩容

StringBuffer

多线程拼接(极少用)

单线程优先用 StringBuilder

String.join()

集合 / 数组 + 固定分隔符

简洁替代循环拼接,无需处理分隔符

StringJoiner

带前缀 / 后缀 / 分隔符的拼接

适配 SQL IN、JSON 数组等场景

Text Blocks

多行字符串(HTML/SQL/JSON)

结合 formatted() 实现插值,提升可读性

String.format()

需格式化的拼接(数字 / 日期)

可读性优先,性能要求高时替换为 StringBuilder

三、避坑指南(关键技巧)

避免循环中用 + 拼接

错误示例:

String str = "";
for (int i = 0; i < 1000; i++) {
    str += i; // 每次循环新建 StringBuilder,性能极差
}

正确示例:

StringBuilder sb = new StringBuilder(1000);
for (int i = 0; i < 1000; i++) {
    sb.append(i);
}
String str = sb.toString();
  1. 预估 StringBuilder 容量

    若能预估最终字符串长度(如拼接 100 个长度为 2 的字符串,总长度约 200),初始化时指定 new StringBuilder(200),避免底层 char 数组扩容(扩容会复制数组,损耗性能)。

  2. 优先用 JDK 内置工具

    集合拼接优先用 String.join(),而非手动循环;多行文本优先用 Text Blocks,而非多个 \n 拼接。

总结

  1. 性能优先:单线程动态 / 循环拼接用 StringBuilder(指定初始容量),少量固定拼接用 + 运算符。

  2. 可读性优先:集合拼接用 String.join(),带前后缀用 StringJoiner,多行文本用 Text Blocks,格式化用 String.format()

  3. 避坑核心:循环中绝对避免 + 拼接,多线程拼接优先优化逻辑(如局部 StringBuilder)而非直接用 StringBuffer