了解 Java 的文本块

一直以来,Java 语言中并没有很好的方式在代码中直接添加多行的字符串常量。

一种常见的做法是使用字符串连接。每一个字符串表示一行,需要手动添加换行符。字符串连接的问题在于很难维护。尤其是需要对双引号进行转义。在有些格式中,比如 YAML,白字符的位置会对语义产生影响。在使用字符串连接时,很难有效确保格式的正确性。

public class StringTemplate {

  public String generate(User user) {
    return "<user>\n"
        + "  <id>" + user.id() + "</id>\n"
        + "  <name>" + user.name() + "</name>\n"
        + "  <email>" + user.email() + "</email>\n"
        + "</user>";
  }
}

另外一种做法是把多行字符串保存在文件中,在运行时读取。这种做法需要额外的代码来实现。

在其他主流语言都支持了文本块的情况下,Java 终于提供了相应的支持。文本块在 Java 12 中以预览功能的形式引入,在 Java 13 中再次预览,在 Java 14 中成为正式功能。

文本块

文本块使用3个双引号来分隔。文本块中的双引号不需要转义。换行符会被保留。

public class SimpleTextBlocks {

  private static final String BLOCK1 = """
      <user>
        <id>001</id>
        <name>alex</name>
        <email>alex@example.com</email>
      </user>
      """;

  private static final String BLOCK2 = """
      {
        "id": "001",
        "name": "alex",
        "email": "alex@example.com"
      }
      """;
}

文本块完全是由编译器来处理的。在运行时,文本块与一般的单行字符串并没有任何区别。

编译器通过3个步骤来处理文本块。

  • 第一步是把操作系统特有的换行符统一转换成 LFCRCRLF 都会被转换成 LF
  • 第二步是删除作为缩进使用的白字符。
  • 最后一步是处理转义字符。

处理缩进白字符

文本块中的白字符可以提高代码的可读性,甚至对语义产生影响。当文本块嵌入在Java代码中时,其中的内容的缩进需要匹配Java代码的格式。

在下面的代码中,文本块的第一和第五行的缩进是6个空格,而其他行的缩进是8个空格。

public class Indentation {

  private static final String BLOCK = """
      <user>
        <id>001</id>
        <name>alex</name>
        <email>alex@example.com</email>
      </user>
      """;
}

对于所有的行来说,有6个空格的缩进是由于 Java 代码的格式而产生的。文本块的实际内容应该如下所示。这就要求从文本块中删除一些空格。

<user>
  <id>001</id>
  <name>alex</name>
  <email>alex@example.com</email>
</user>

文本块中每一行的后缀白字符会被自动删除。而前缀白字符则使用特定的算法来删除。这个算法其实很简单,一共就两条规则。

  • 第一条规则是从每一行删除同样数量的白字符。
  • 第二条规则是所删除的白字符的数量是所有行的前缀白字符数量的最小值

回到之前的文本块的例子,所有行的前缀白字符的最小数量是6,因此每一行都会被删除6个前缀字符,就得到了实际所需的结果。

需要注意的是在文本块末尾出现的空白行。这个空白行的缩进会影响到最终的处理结果。

在下面的代码中,末尾的空白行有6个空格,是所有行中最小的。因此实际被删除的白字符是6个。所得到结果的第一行的缩进是4个字符。

public class Indentation2 {

  private static final String BLOCK = """
          <user>
            <id>001</id>
            <name>alex</name>
            <email>alex@example.com</email>
          </user>
      """;
}

转义序列

文本块中可以使用字符串常量所支持的全部转义序列。除此之外,还增加了两个新的转义序列。

第一个转义序列是 \ 后加上换行符,用来阻止插入换行符。可以用来生成很长的单行字符串。下面代码中的 LONG_STRING 中并没有换行符。

public class LongString {

  private static final String LONG_STRING = """
      This a very long string. \
      This string continues. \
      The last part of this string. \
      """;
}

第二个转义序列是 \s,表示一个空格。在文本块中可以辅助格式化。可以在每一行的末尾添加 \s,那么 \s 之前的白字符都会保留,而 \s 之后的白字符则会被删除。下面代码中的文本块中的每一行的长度都是11。

public class KeepWhiteSpace {

  private static final String BLOCK = """
      alex:    1\s
      bob:    12\s
      david: 100\s
      """;
}

String 的新方法

String 中添加了3个新的方法。

stripIndent() 的作用是去掉作为缩进的白字符,实现了之前所说的编译器处理文本块时的缩进删除算法。

translateEscapes() 的作用是对字符串中的转义序列进行处理。

formatted() 的作用是以当前 String 对象作为模式来进行格式化。

下面的代码展示了文本块的 formatted 方法的使用。

public class FormatStrings {

  public String format(User user) {
    return """
        <user>
          <id>%s</id>
          <name>%s</name>
          <email>%s</email>
        </user>
        """.formatted(user.id(), user.name(), user.email());
  }

关于文本块的内容,就介绍到这里。相应的视频见我的B站

版权所有 © 2024 灵动代码