JDK 15 引入了多行字符串字面量(Text Blocks)特性,不再需要采用字符串拼接的方式来在 Java 代码中定义多行字符串,同时也不需要对字符串内部的双引号做转义,让代码有更好的可读性和可维护性。
使用
定义一个 Text Block 很简单,采用三个双引号 """
(开始分隔符)来开头,后面可跟空白符,然后换行开始输入内容,可以换行或输入直接输入双引号,最后用三个双引号"""
(结束分隔符)结束。
Text Block 的内容从开始分隔符后第一个换行符的下一个字符串开始,到结束分隔符前一个字符结束。
var s = """
<html>
<body>
<p>Hello, world!</p>
</body>
</html>
""";
最后将得到,其中用.
来代表空格,可以看到保持了多行,并且保持了缩进(注意,最后有一个空行)。
<html>
....<body>
........<p>Hello,.world!</p>
....</body>
</html>
虽然定义形式上和传统的字符串有所区别,但编译后就是 String
类型,所以可以用在所有传统字符串能出现的地方,比如用 +
来连接字符串。
var s = """
line1
line2
line3
""" + "line4";
缩进
前面提到 text block 会保留缩进,但不是保留所有的空白,不是真正的“字面量”,编译器会在编译时对每行的前后空白符做处理。
处理的主要流程是:
- 换行符统一转换为
<LF>
,避免因为类似 windows 默认使用<CR><LF>
来换行而导致不同系统下内容不一致的问题 - 根据所有非空白行以及最后的行来确定字符串左边(leading)最大的公共空白数量(空格、tab 等都当作一个字符来处理,不因展示上占用的空间不同而不同),然后每行移除左边该数量的空白符。再移除每行尾部所有的空白符。具体见 String.stripIndent 方法。
- 前面两个阶段不会对对转义符做处理,在处理完缩进后才使用 String.translateEscape来做处理。
var s = """
line1
line2
line3
line4"""; // line4 字符前面是一个 tab
实际内容是
...line1
.......line2
.line3
line4
Text block 的最后一行,如果是空行,那么会参数计算左边最大空白数量。所以,如果结束分隔符比较靠左,那么对实际内容的缩进影响可能比较明显。
var s = """
line1
line2
line3
line4
""";
实际的内容如下(最后有一个空行):
....line1
....line2
....line3
....line4
转义
Text block 内部可以直接使用 "
而不需要使用 \"
来转义,除了直接在结束分隔符("""
)前的。
System.out.println("""
1 "
2 ""
3 ""\"
4 ""\""
5 ""\"""
6 ""\"""\"
7 ""\"""\""
8 ""\"""\"""
9 ""\"""\"""\"
10 ""\"""\"""\""
11 ""\"""\"""\"""
12 ""\"""\"""\"""\"
""");
除了已有的 \b
和 \n
等转义符 外,新增了 \s
和 \<line-terminator>
这两个转义符。\s
表示一个空格(\u0020
),\<line-terminator>
表示展示上换行但内容不换行(处理时该转义符被移除)。