概述

  1. JDK 15
  2. 文字块 - 一个由多行文字构成的字符串

复杂字符串

需要处理 - 文本对齐、换行字符、连接符以及双引号的转义字符串 - 不美观 + 不简约 + 不自然

1
2
3
4
5
6
7
String stringBlock =
"<!DOCTYPE html>\n"
+ "<html>\n"
+ " <body>\n"
+ " <h1>\"Hello World!\"</h1>\n"
+ " </body>\n"
+ "</html>\n";

所见即所得的文字块

  1. 文字块是一个由多行文件构成的字符串
  2. 文字块使用新的形式,尝试消除换行符连接符转义字符的影响
    • 使得文字对齐必要的占位符更加清晰,从而简化多行文字字符串的表达

image-20250802162412991

  1. 换行符 \n 没有出现在文字块这个
  2. 连字符 + 没有出现在文字块这个
  3. 双引号没有使用转义字符 \

与 Python 类似

  1. 文字块由零个多个内容字符组成
  2. 开始分隔符开始,到结束分隔符结束 - """
    • 开始分隔符由 """ 开始,后面跟着零个多个空格,以及行结束符组成的序列
      • 开始分隔符必须单独成行,前面的 """ 以及后面的空格换行符都属于开始分隔符
      • 因此,一个文字块至少有两行代码
      • 即使是一个字符串,结束分隔符也不能和开始分隔符放在同一行代码
    • 结束分隔符由 """ 组成的序列
1
2
3
4
5
6
7
8
9
10
jshell> String s = """""";
| Error:
| illegal text block open delimiter sequence, missing line terminator
| String s = """""";
| ^

jshell> String s = """
...> """;
s ==> ""
| created variable s : String

结束分隔符只有一个由 """ 组成的序列,在这之前的字符,包括换行符,都属于文字块的有效内容

1
2
3
4
5
6
7
8
9
10
11
jshell> String s = """
...> Oneline""";
s ==> "Oneline"
| created variable s : String

jshell> String s = """
...> Twolines
...> """;
s ==> "Twolines\n"
| modified variable s : String
| update overwrote variable s : String
  1. 由于文字块不再需要特殊字符,几乎可以直接拷贝粘贴看到的文字,不再需要特殊处理
  2. 代码中看到的文字块的样子,就是其实际要表达的样子 - 所见即所得

文字块的编译过程

为了代码整洁而使用的缩进空格并没有出现在打印的结果里 - 文本块的内容并没有计入缩进空格

1
2
3
4
5
6
7
Here is the text block:
<!DOCTYPE html>
<html>
<body>
<h1>"Hello World!"</h1>
</body>
</html>

与传统字符串一样,文字块是字符串的一种常量表达式
不同于传统字符串,在编译期间,文字块要顺序通过三个不同的编译步骤

  1. 为了降低不同平台换行符表达差异
    • 编译器把文字内容里的换行符统一转换成 LF - \u000A
  2. 为了能够处理 Java 源代码里缩进空格
    • 要删除所有文字内容行和结束分隔符共享前导空格,以及所有文字内容行的尾部空格
  3. 最后处理转义字符
    • 开发人员编写的转义序列不会在第一步和第二步被修改或删除

image-20250802170136464

  1. 使用传统方式声明的字符串和使用文字块声明的字符串,它们的内容是一样的,并且指向同一个对象
  2. 文字块是在编译期处理的,并且在编译期被转换常量字符串,然后被当作常规字符串
  3. 如果文字块代表的内容,和传统字符串代表的内容是一样的
    • 那么这两个常量字符串变量指向同一个内存地址,代表同一个对象
  4. 虽然表达形式不同,但文字块就是字符串 - 能够使用字符串支持的各种 API操作方法

混合使用

image-20250802170950324

文字块可以调用字符串 String 的 API

1
2
3
4
5
6
7
8
9
10
int stringSize =
"""
<!DOCTYPE html>
<html>
<body>
<h1>"Hello World!"</h1>
</body>
</html>
"""
.length();

使用嵌入式的表达式

image-20250802171236113

巧妙的结束分隔符

删除共享的前导空格 - 通过合理地安排共享的前导空格,可以实现文字的编排缩进
. 表示编译期要删除的前导空格! 表示编译期要删除的尾部空格

Case 1

把结束分隔符单独放在一行,和文本内容左边对齐
此时,共享的前导空格就是文本内容本身共享的前导空格,结束分隔符仅仅用来结束文字块

1
2
3
4
5
6
7
8
9
// There are 8 leading white spaces in common
String textBlock = """
........<!DOCTYPE html>
........<html>
........ <body>
........ <h1>"Hello World!"</h1>!!!!
........ </body>
........</html>
........""";

Case 2

把结束分隔符单独放一行,但放在比文本内容更靠左的位置
此时,结束分隔符除了用来结束文字块之外,还参与界定共享的前导空格

1
2
3
4
5
6
7
8
9
// There are 4 leading white spaces in common
String textBlock = """
.... <!DOCTYPE html>
.... <html>
.... <body>
.... <h1>"Hello World!"</h1>!!!!
.... </body>
.... </html>
....""";

Case 3

把结束分隔符单独放一行,但放在比文件内容左对齐位置的右侧
此时,结束分隔符的左侧,除了共享的前导空格之外,还有多余的空格
这些多余的空格,本质上是文字内容行的尾部空格,它们会在编译期被删除掉

1
2
3
4
5
6
7
8
9
// There are 8 leading white spaces in common
String textBlock = """
........<!DOCTYPE html>
........<html>
........ <body>
........ <h1>"Hello World!"</h1>!!!!
........ </body>
........</html>
........!!!!""";

保留尾部空格

  1. 为了能够支持尾部附带的空格,文字块引入了另一个新的转义字符 - \s 表示一个空格
  2. 空格转义符不会在文字块的编译期被删除,因此空格转义符之前的空格也能被保留 - 每行使用一个 \s 即可

前两行保留尾部空格

1
2
3
4
5
6
7
8
9
// There are 8 leading white spaces in common
String textBlock = """
........<!DOCTYPE html> \s!!!!
........<html> \s
........ <body>!!!!!!!!!!
........ <h1>"Hello World!"</h1>
........ </body>
........</html>
........""";

长段落

  1. 编码规范一般都限定每一行字节数 - 80/120 - 一个文本的长段落通常要超出这个限制
  2. 文字块引入了新的转义字符 - 行终止符 - 换行转义符
    • 如果换行转义符出现在一个行的结束位置,那么这一行的换行会被取缔

image-20250802173517715