Java 中的文本块(或多行字符串)功能是什么?

What is the Text Blocks (or Multiline Strings) feature in Java?

Java SE 13 引入了 Text Blocks(或多行字符串)功能。它与现有的字符串表示有何异同?

什么是文本块?

A text block 是一个多行字符串文字,该功能提供了一种干净的方式来以可预测的方式格式化字符串,而无需使用大部分转义序列。它以 """(三个双引号)开头和结尾,例如

public class Main {
    public static void main(String[] args) {
        String text = """
                <html>
                    <head>
                        <title>Hello World</title>
                    </head>
                    <body>
                        Java rocks!
                    </body>
                </html>""";

        System.out.println(text);
    }
}

输出:

<html>
    <head>
        <title>Hello World</title>
    </head>
    <body>
        Java rocks!
    </body>
</html>

使用传统的字符串表示,代码看起来像

public class Main {
    public static void main(String[] args) {
        String text = "<html>\n"
                + " <head>\n"
                + "     <title>Hello World</title>\n"
                + " </head>\n"
                + " <body>\n"
                + "     Java rocks!\n"
                + " </body>\n"
                + "</html>";

        System.out.println(text);
    }
}

另一个主要区别是文本块以三个双引号字符后跟一个行终止符开头 这与传统的字符串表示形式不同。意思是

  1. 文本块不能放在一行。

  2. 文本块的内容不能跟在同一行的三个左双引号后面。

    String str = "Hello World!"; // The traditional string
    
    String textBlock = """
            Hello World!
            """; // The text block
    
    String notAllowed = """Hello World!"""; // Error
    
    // The following code will fail compilation
    String notAllowed2 ="""Hello
             World!
            """;
    

这是 Java 中急需的功能。

关于它与 Kotlin(一种基于 JVM 的现代语言)中文本块的比较的注意事项?

  • Kotlin 没有上述两个约束中的任何一个。
  • Kotlin 不需要在文本块内转义 \,这使得编写 RegEx 表达式更容易,例如以下是 Kotlin 中的有效文本块,但在 Java:
  • 中无效
    var str = """
        \d{2}-\d{2}-\d{4}
        """

在Java中,必须写成

var str = """
    \d{2}-\d{2}-\d{4}
    """;

关于缩进的注意事项:

编译器将整个文本块向左移动,然后保留指定的间距。

String str1 = """
           Hello
        """;
        ^^^<-----The three whitespace characters before Hello will be retained

演示:

public class Main {
    public static void main(String[] args) {
        // Text block without a line break at the end
        String str1 = """
                Hello""";

        // Text block with a line break at the end
        String str2 = """
                Hello
                """;

        // Text block with three whitespace in the beginning and a line break at the end
        String str3 = """
                   World!
                """;
        System.out.println(str1);
        System.out.println(str2);
        System.out.println(str3);
        System.out.println("Java rocks!");
    }
}

输出:

Hello
Hello

   World!

Java rocks!

它是否仅作为预览功能提供?

它在 Java SE 13 和 Java SE 14 中作为 预览功能 仍然可用,并已通过 Java SE 15 标准化. 对于 Java SE 13 和 14,与任何 预览功能 一样,它必须使用 --enable-preview 选项编译和执行,例如

编译:

javac --enable-preview --release 13 Main.java

执行:

java --enable-preview Main

它们存储在字符串池中吗?

是的,他们是。 文本块被编译为与传统String值相同的类型,即字节码不区分传统String值和文本块.

public class Main {
    public static void main(String[] args) {
        String str1 = "Hello World!";
        String str2 = """
                Hello World!""";
        System.out.println(str1 == str2);
    }
}

输出:

true

文本块可以与另一个字符串连接吗?

是的,一个文本块可以连接到一个传统的字符串值或另一个 文本块,以相同的方式连接传统的 String 值。如上所述,字节码不区分传统的 String 值和 文本块 .

public class Main {
    public static void main(String[] args) {
        String str1 = "Hello ";
        String str2 = """
                World!""";
        String str3 = """
                 Java rocks!
                """;
        System.out.println(str1 + str2);
        System.out.println(str1 + (str2 + str3));
    }
}

输出:

Hello World!
Hello World! Java rocks!

请注意,我在 str1 中的 Hello 之后放置了空格,在 str3 中的 Java rocks! 之前放置了另一个空格。

是否支持转义序列

是的,转义序列将以传统方式计算,例如

public class Main {
    public static void main(String[] args) {
        String text = """
                <html>
                    <head>
                        <title>Hello World</title>
                    </head>
                    <body>
                        Java\n\t\trocks!
                    </body>
                </html>""";

        System.out.println(text);
    }
}

输出:

<html>
    <head>
        <title>Hello World</title>
    </head>
    <body>
        Java
        rocks!
    </body>
</html>

是否支持可替换参数?

是的,您可以使用 %s$<<replaceable-parameter>> 替换文本块中的参数,如下所示:

public class Main {
    public static void main(String[] args) {
        String text = """
                What is the distance between %s and %s?""";

        System.out.println(String.format(text, "earth", "moon"));
        System.out.println(String.format(text, "earth", "mercury"));

        // Alternative-1
        text = """
                What is the distance between $celestial1 and $celestial2?""";

        System.out.println(text.replace("$celestial1", "earth").replace("$celestial2", "moon"));

        // Alternative-2 using the deprecated String#formatted
        text = """
                What is the distance between %s and %s?""";
        System.out.println(text.formatted("earth", "moon"));
    }
}

输出:

What is the distance between earth and moon?
What is the distance between earth and mercury?
What is the distance between earth and moon?
What is the distance between earth and moon?