1. 概述

在之前的教程中,我们已经了解了如何在任意 Java 版本中使用多行字符串(比如通过拼接或 StringBuilder)。

本文将深入讲解 Java 15 正式引入的 Text Blocks 功能,它让我们声明多行字符串的方式更加简洁高效,尤其适合嵌入 HTML、JSON、SQL 等结构化文本。

✅ 从 Java 15 开始,Text Blocks 已是标准功能。
⚠️ 在 Java 13 和 14 中属于预览特性(preview feature),需手动开启。


2. 基本用法

Text Blocks 使用三个双引号 """ 开始,后跟可选空格和换行符。语法非常直观:

String example = """
     Example text""";

📌 关键点:

  • ✅ 结果类型依然是 String,只是字面量写法变了。
  • ✅ 可自由使用换行和双引号,无需转义。
  • ✅ 自动处理首行缩进和首尾空白,提升可读性。

举个实际例子:写一段 HTML 片段再也不用拼接了:

String html = """
        <html>
            <body>
                <p>Hello "world"!</p>
            </body>
        </html>""";

对比传统写法:

String htmlOld = "<html>\n" +
                 "    <body>\n" +
                 "        <p>Hello \"world\"!</p>\n" +
                 "    </body>\n" +
                 "</html>";

一眼就能看出,Text Blocks 更清晰、不易出错,尤其在复杂字符串场景下简直是“降维打击”。


3. 缩进处理机制

这是很多人刚用 Text Blocks 时容易踩坑的地方:编译器会自动去除“公共最小缩进”

工作原理

  1. 扫描所有非空行,找出最左边的缩进量(以空格计)
  2. 将整个文本块整体左移该缩进量
  3. 第一个换行也会被自动去除

看这个例子:

public String getBlockOfHtml() {
    return """
            <html>

                <body>
                    <span>example text</span>
                </body>
            </html>""";
}

其中 <html> 行有 12 个空格缩进,且是所有非空行中最少的,因此编译器会把每行都左移 12 个空格。

最终生成的字符串等价于:

"<html>\n\n    <body>\n        <span>example text</span>\n    </body>\n</html>"

我们可以通过单元测试验证:

@Test
void givenAnOldStyleMultilineString_whenComparing_thenEqualsTextBlock() {
    String expected = "<html>\n"
      + "\n" 
      + "    <body>\n"
      + "        <span>example text</span>\n"
      + "    </body>\n"
      + "</html>";
    assertThat(subject.getBlockOfHtml()).isEqualTo(expected);
}

如何保留特定缩进?

如果你想保留比“最小缩进”更多的空格,可以在某行减少缩进来“锚定”更小的公共缩进。

例如:

public String getNonStandardIndent() {
    return """
                Indent
            """;
}

这里 " " 有 12 个空格,但内容行只有 4 个,所以最小缩进是 4。结果为:

"    Indent\n"

✅ 小技巧:如果想让内容完全顶格,可以在最后一行加一个空行并缩进回来:

return """
    line1
    line2
    """;
// 结果:"line1\nline2\n"

4. 转义规则

虽然 Text Blocks 减少了转义需求,但某些场景仍需手动控制。

4.1 双引号转义

✅ 在 Text Blocks 中,双引号无需转义!

String text = """
    He said: "Hello, world!"
    """;

❌ 但如果你写了三个双引号 """,就必须转义其中一个,否则会被误认为是结束符:

public String getTextWithEscapes() {
    return """
            "fun" with
            whitespace
            and other escapes \"""
            """;
}

⚠️ 注意:虽然可以写 \",但在 Text Blocks 中这样做属于反模式——既然提供了更优雅的写法,何必自找麻烦?


4.2 换行符转义

✅ 默认情况下,换行符无需转义,会自动归一化为 \n

⚠️ **即使源文件使用 Windows 换行符(\r\n),Text Blocks 也只会生成 \n**。

如果需要保留 \r,必须显式写出:

public String getTextWithCarriageReturns() {
    return """
separated with\r
carriage returns""";
}

测试验证:

@Test
void givenATextWithCarriageReturns_thenItContainsBoth() {
    assertThat(subject.getTextWithCarriageReturns())
        .isEqualTo("separated with\r\ncarriage returns");
}

忽略物理换行(逻辑合并)

有时候代码里为了可读性想把长字符串拆行,但希望运行时合并成一行。可以用 \ 忽略换行:

public String getIgnoredNewLines() {
    return """
            This is a long test which looks to \
            have a newline but actually does not""";
}

✅ 实际结果是一整行:

@Test
void givenAStringWithEscapedNewLines_thenTheResultHasNoNewLines() {
    String expected = "This is a long test which looks to have a newline but actually does not";
    assertThat(subject.getIgnoredNewLines())
            .isEqualTo(expected);
}

4.3 空格转义

✅ 编译器默认会删除所有行尾空格(trailing spaces),这是为了防止无意引入空白字符。

但从 Java 14 预览开始,支持使用 \s 保留空格:

public String getEscapedSpaces() {
    return """
            line 1·······
            line 2·······\s
            """;
}

📌 说明:上面的 · 是为了可视化空格而添加的标记,实际代码中是空格。

结果:

"line 1\nline 2        \n"

解释:

  • 第一行末尾的空格被删掉了
  • 第二行用了 \s,所以前面的所有空格都被保留

⚠️ 注意:\s 不代表“一个空格”,而是“保留此处之前的连续空格”,常用于格式对齐或模板填充。


5. 字符串格式化

Java 15 还为 String 类新增了一个实用方法:.formatted(),可以直接在文本块上调用变量替换。

public String getFormattedText(String parameter) {
    return """
            Some parameter: %s
            """.formatted(parameter);
}

等价于:

String.format("""
            Some parameter: %s
            """, parameter);

.formatted() 更简洁,链式调用也更自然,推荐优先使用。


6. 总结

Text Blocks 虽然不是颠覆性特性,但在日常开发中极大提升了代码可读性和维护性,尤其是在处理:

  • SQL 查询语句
  • JSON 模板
  • HTML/XML 片段
  • Shell 脚本嵌入

✅ 推荐使用场景:

场景 推荐程度
多行 SQL ⭐⭐⭐⭐⭐
内联 JSON ⭐⭐⭐⭐☆
日志模板 ⭐⭐⭐☆☆
简单拼接 ⭐☆☆☆☆(没必要)

🚀 小建议:升级到 Java 15+ 后,遇到多行字符串优先考虑 Text Blocks,简单粗暴又安全。

完整示例代码已托管至 GitHub:https://github.com/baomidou/tutorials/tree/master/core-java-15


原始标题:Java Text Blocks