Java16中的新特性——文本块

概述

Java 16 于 2021 年 3 月发布,其中包含了一系列新特性和改进。在这些更新中,文本块(Text Blocks)作为正式特性的正式引入是一项重要的改进,它极大地提高了处理多行字符串的便捷性和可读性。下面将详细介绍 Java 16 中文本块的相关内容。

文本块解决了Java长期以来的一个痛点:处理多行字符串时的繁琐和可读性问题。在文本块出现之前,开发者需要在每行末尾添加换行符(\n),使用加号(+)连接字符串,或者利用反斜杠(\)来延续字符串并处理大量转义字符,这导致代码难以阅读和维护。

文本块的引入不仅简化了多行字符串的表示,还显著提升了代码可读性,特别在处理HTML、JSON、SQL等结构化文本时效果尤为明显。这项特性最初在Java 13和14中作为预览功能推出,经过社区反馈和改进后,终于在Java 16中成为正式特性。

文本块的语法

文本块使用三个双引号(""")作为界定符,中间可以直接包含多行文本。其语法格式如下:

1
2
3
4
5
String text = """
这是一个文本块示例。
它可以包含多行文本,
无需在每行末尾添加换行符。
""";

文本块的设计遵循以下核心原则:

  1. 提高可读性:消除多行字符串中的转义字符和连接符
  2. 保留格式:保持文本的原始缩进和换行结构
  3. 简化开发:减少处理结构化文本(JSON/XML/SQL)的认知负担
  4. 兼容性:确保与现有字符串类型完全兼容

与传统字符串的比较

下面通过一个对比示例,来看一下文本块与传统字符串在处理多行文本时的差异。

传统方式处理JSON:

1
2
3
4
5
6
7
8
String json = "{\n" +
" \"name\": \"John\",\n" +
" \"age\": 30,\n" +
" \"address\": {\n" +
" \"street\": \"123 Main St\",\n" +
" \"city\": \"New York\"\n" +
" }\n" +
"}";

使用文本块方式:

1
2
3
4
5
6
7
8
9
10
String json = """
{
"name": "John",
"age": 30,
"address": {
"street": "123 Main St",
"city": "New York"
}
}
""";

可以明显看出,使用文本块后,代码更加简洁易读。

文本块的优势

  1. 提升可读性:文本块无需在每行添加换行符和连接符号,使代码结构更加清晰,更易于阅读和理解。
  2. 减少转义字符的使用:在文本块中,一些常见的转义字符可以直接使用,无需额外转义,例如双引号(")。
  3. 保留格式:文本块会保留文本的原始格式,包括缩进和换行,这在处理 HTML、JSON、SQL 等格式的文本时非常有用。

其中的使用细节和高级特性

  1. 缩进处理 文本块会自动处理缩进,以保证代码的美观。它会以开始标记后的第一个非空行为基准,自动调整文本的缩进。例如:

    1
    2
    3
    4
    5
    6
    7
    String html = """
    <html>
    <body>
    <p>Hello, World!</p>
    </body>
    </html>
    """;

    在这个例子中,文本块内部的 HTML 代码保持了正确的缩进,而不会在最终的字符串中引入额外的缩进。

  2. 增强的转义处理

    虽然文本块减少了一些转义字符的使用,但在需要时,仍然可以使用传统的转义字符,如 \n(换行)、\t(制表符)等。此外,文本块还引入了两个新的转义序列:

    • \s:表示一个空格。
    • \"\":表示一个双引号,无需像传统字符串那样使用 \" 来转义
    1
    2
    3
    4
    5
    String text = """
    名字: "张三"
    特殊字符: \"""
    保留末尾空格:\s
    """;
  3. 动态内容嵌入

    文本块支持多种动态内容嵌入方式:

    文本块可以与其他字符串进行连接,使用加号(+)即可。例如

    1
    2
    3
    4
    5
    6
    String name = "Alice";
    String message = """
    你好,
    """ + name + """
    ! 欢迎使用文本块。
    """;

    也可以使用格式化方法

    对于动态内容,优先使用formatted()而非字符串连接

    1
    2
    3
    4
    5
    6
    7
    String item = "笔记本";
    int quantity = 3;
    String order = """
    订单明细:
    - 商品: %s
    - 数量: %d
    """.formatted(item, quantity);
  4. 空行与空白处理

    • 开头的空行会被忽略

    • 末尾空行会被包含

    • 行尾空白默认被移除(使用\s保留)

      1
      2
      3
      4
      5
      6
      String example = """

      第一行(前面有空行)
      第二行(后面有空格)\s

      """;
  5. 包含转义字符的文本块

    内部包含转义字符的文本块很特殊,下面一个示例看一下

    1
    2
    3
    4
    5
    6
    7
    8
    String text = """
    这是一个包含转义字符的文本块示例:
    - 换行符:\n
    - 制表符:\t
    - 双引号:\"\"
    - 反斜杠:\\
    """;
    System.out.println(text);

    在这个示例中,我们展示了如何在文本块中使用各种转义字符。需要注意的是,双引号(")在文本块中无需转义,除非需要表示连续的三个双引号("""),此时应使用 \"\"

  6. 性能考量

    文本块在编译时被处理为普通字符串,因此运行时性能与传统字符串完全相同。编译器会将文本块转换为常规字符串,移除公共缩进并处理转义序列,不会增加运行时开销。但是还是要避免大文本块,超过64KB的文本块会影响编译器性能

文本块的 API 支持

Java 16 还为文本块提供了一些新的 API 方法,这些方法可以帮助开发人员更好地处理和转换文本块。

  1. stripIndent() 方法

    该方法用于移除文本块中每一行的公共前导空格,从而保留文本的相对缩进。

    1
    2
    3
    4
    5
    6
    7
    String indentedText = """
    这是一个有缩进的文本块。
    这一行有额外的缩进。
    这一行回到了原来的缩进级别。
    """;
    String strippedText = indentedText.stripIndent();
    System.out.println(strippedText);
  2. translateEscapes() 方法

    该方法用于解析文本块中的转义序列,将其转换为对应的字符。

    1
    2
    3
    4
    5
    6
    7
    String escapedText = """
    这是一个包含转义字符的文本:
    换行符:\\n
    制表符:\\t
    """;
    String translatedText = escapedText.translateEscapes();
    System.out.println(translatedText);
  3. formatted() 方法

    该方法用于格式化文本块,类似于 String.format() 方法。

    1
    2
    3
    4
    5
    6
    7
    String name = "Bob";
    int age = 35;
    String message = """
    你好,%s!
    你今年 %d 岁了。
    """.formatted(name, age);
    System.out.println(message);

实际应用场景

1. JSON/XML处理

1
2
3
4
5
6
7
8
String productJson = """
{
"id": 101,
"name": "无线耳机",
"price": 299.99,
"inStock": true
}
""";

2. SQL查询

1
2
3
4
5
6
7
String query = """
SELECT o.order_id, c.customer_namejava
FROM orders o
JOIN customers c ON o.customer_id = c.customer_id
WHERE o.order_date > '2023-01-01'
ORDER BY o.order_date DESC
""";

3. HTML模板

1
2
3
4
5
6
7
8
9
String emailTemplate = """
<html>
<body>
<h1>尊敬的%s,</h1>
<p>您的账户余额为:<strong>¥%.2f</strong></pjava>
%s
</body>
</html>
""".formatted(userName, balance, getPromotionSection());

4. 测试数据

1
2
3
4
5
6
String csvData = """
ID,Name,Age,Occupation
1,张三,28,工程师
2,李四,32,设计师
3,王五,25,分析师
""";