Java是否支持多行字符串?

来自Perl,我肯定错过了在源代码中创建多行字符串的“here-document”方法:

$string = <<"EOF"  # create a three-line string
text
text
text
EOF

在Java中,当我从头开始连接多行字符串时,我必须在每一行上使用繁琐的引号和加号。

有什么更好的选择吗?在属性文件中定义我的字符串?

编辑:两个答案说StringBuilder.append()比加号更可取。谁能详细解释一下他们为什么这么想?在我看来,这一点也不可取。我正在寻找一种方法来解决多行字符串不是一级语言结构这一事实,这意味着我绝对不想用方法调用取代一级语言结构(字符串连接与加号)。

编辑:为了进一步澄清我的问题,我根本不关心性能。我关心的是可维护性和设计问题。

709691 次浏览

我所知道的唯一方法是用加号连接多行

String newline = System.getProperty ("line.separator");
string1 + newline + string2 + newline + string3

但是,最好的替代方法是使用String.format

String multilineString = String.format("%s\n%s\n%s\n",line1,line2,line3);

另一种方法是将长字符串存储在外部文件中,然后将文件读入字符串。

你可以在一个单独的方法中连接你的追加:

public static String multilineString(String... lines){
StringBuilder sb = new StringBuilder();
for(String s : lines){
sb.append(s);
sb.append ('\n');
}
return sb.toString();
}

无论哪种方式,使用StringBuilder而不是加号。

在属性文件中定义我的字符串?

属性文件中不允许多行字符串。您可以在属性文件中使用\n,但我认为这在您的情况下不是一个很好的解决方案。

如果你在属性文件中定义字符串,它看起来会更糟糕。IIRC,它看起来像:

string:text\u000atext\u000atext\u000a

一般来说,在源代码中不嵌入大字符串是一个合理的想法。您可能希望将它们作为资源加载,可能以XML或可读的文本格式加载。文本文件可以在运行时读取,也可以编译成Java源代码。如果你最终把它们放在源代码中,我建议把+放在前面,省略不必要的新行:

final String text = ""
+"text "
+"text "
+"text"
;

如果你有新的行,你可能需要一些join或格式化方法:

final String text = join("\r\n"
,"text"
,"text"
,"text"
);

一个非常高效且与平台无关的解决方案是使用系统属性的行分隔符和StringBuilder类来构建字符串:

String separator = System.getProperty("line.separator");
String[] lines = {"Line 1", "Line 2" /*, ... */};


StringBuilder builder = new StringBuilder(lines[0]);
for (int i = 1; i < lines.length(); i++) {
builder.append(separator).append(lines[i]);
}
String multiLine = builder.toString();

请注意:这个答案适用于Java 14及以上版本。

Java 15引入了文本块(多行文字)。详见这个答案


听起来像是要做一个多行文字,这在Java中是不存在的。

你最好的选择是将字符串# eyz0d放在一起。人们提到的一些其他选项(StringBuilder, String。format, String.join)只有当你从一个字符串数组开始时才更可取。

考虑一下:

String s = "It was the best of times, it was the worst of times,\n"
+ "it was the age of wisdom, it was the age of foolishness,\n"
+ "it was the epoch of belief, it was the epoch of incredulity,\n"
+ "it was the season of Light, it was the season of Darkness,\n"
+ "it was the spring of hope, it was the winter of despair,\n"
+ "we had everything before us, we had nothing before us";

与# EYZ0:

String s = new StringBuilder()
.append("It was the best of times, it was the worst of times,\n")
.append("it was the age of wisdom, it was the age of foolishness,\n")
.append("it was the epoch of belief, it was the epoch of incredulity,\n")
.append("it was the season of Light, it was the season of Darkness,\n")
.append("it was the spring of hope, it was the winter of despair,\n")
.append("we had everything before us, we had nothing before us")
.toString();

与# EYZ0:

String s = String.format("%s\n%s\n%s\n%s\n%s\n%s"
, "It was the best of times, it was the worst of times,"
, "it was the age of wisdom, it was the age of foolishness,"
, "it was the epoch of belief, it was the epoch of incredulity,"
, "it was the season of Light, it was the season of Darkness,"
, "it was the spring of hope, it was the winter of despair,"
, "we had everything before us, we had nothing before us"
);

相对于Java8 String.join():

String s = String.join("\n"
, "It was the best of times, it was the worst of times,"
, "it was the age of wisdom, it was the age of foolishness,"
, "it was the epoch of belief, it was the epoch of incredulity,"
, "it was the season of Light, it was the season of Darkness,"
, "it was the spring of hope, it was the winter of despair,"
, "we had everything before us, we had nothing before us"
);

如果您希望在特定的系统中使用换行符,则需要使用System.lineSeparator(),或者可以在String.format中使用%n

另一种选择是将资源放在文本文件中,只读取该文件的内容。对于非常大的字符串,这将是可取的,以避免不必要地膨胀您的类文件。

遗憾的是,Java没有多行字符串字面量。您要么必须连接字符串字面量(使用+或StringBuilder是最常见的两种方法),要么从单独的文件中读取字符串。

对于大的多行字符串文字,我倾向于使用一个单独的文件,并使用getResourceAsStream() (Class类的一个方法)读取它。这使得查找文件变得很容易,因为您不必担心当前目录与代码安装的位置。它还使打包更容易,因为您实际上可以将文件存储在jar文件中。

假设你在一个名为Foo的类中。就像这样做:

Reader r = new InputStreamReader(Foo.class.getResourceAsStream("filename"), "UTF-8");
String s = Utils.readAll(r);

另一个烦恼是Java没有标准的“将这个Reader中的所有文本读入字符串”方法。写起来很简单:

public static String readAll(Reader input) {
StringBuilder sb = new StringBuilder();
char[] buffer = new char[4096];
int charsRead;
while ((charsRead = input.read(buffer)) >= 0) {
sb.append(buffer, 0, charsRead);
}
input.close();
return sb.toString();
}

加号被转换为StringBuilder。除非两个字符串都是常量,以便编译器可以在编译时将它们组合在一起。至少,Sun的编译器是这样的,我怀疑大多数(如果不是所有)其他编译器也会这样做。

所以:

String a="Hello";
String b="Goodbye";
String c=a+b;

通常生成完全相同的代码:

String a="Hello";
String b="Goodbye":
StringBuilder temp=new StringBuilder();
temp.append(a).append(b);
String c=temp.toString();

另一方面:

String c="Hello"+"Goodbye";

等于:

String c="HelloGoodbye";

也就是说,为了可读性,在多行中使用加号将字符串字面量分开并不会受到惩罚。

当使用一长串的+时,只会创建一个StringBuilder,除非在编译时确定String,在这种情况下不使用StringBuilder !

StringBuilder唯一更高效的情况是使用多个语句构造String。

String a = "a\n";
String b = "b\n";
String c = "c\n";
String d = "d\n";


String abcd = a + b + c + d;
System.out.println(abcd);


String abcd2 = "a\n" +
"b\n" +
"c\n" +
"d\n";
System.out.println(abcd2);

注意:只创建了一个StringBuilder。

  Code:
0:   ldc     #2; //String a\n
2:   astore_1
3:   ldc     #3; //String b\n
5:   astore_2
6:   ldc     #4; //String c\n
8:   astore_3
9:   ldc     #5; //String d\n
11:  astore  4
13:  new     #6; //class java/lang/StringBuilder
16:  dup
17:  invokespecial   #7; //Method java/lang/StringBuilder."<init>":()V
20:  aload_1
21:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
24:  aload_2
25:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
28:  aload_3
29:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
32:  aload   4
34:  invokevirtual   #8; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
37:  invokevirtual   #9; //Method java/lang/StringBuilder.toString:()Ljava/lang/String;
40:  astore  5
42:  getstatic       #10; //Field java/lang/System.out:Ljava/io/PrintStream;
45:  aload   5
47:  invokevirtual   #11; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
50:  ldc     #12; //String a\nb\nc\nd\n
52:  astore  6
54:  getstatic       #10; //Field java/lang/System.out:Ljava/io/PrintStream;
57:  aload   6
59:  invokevirtual   #11; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
62:  return

为了进一步澄清我的问题,我根本不关心性能。我关心的是可维护性和设计问题。

尽你所能让它清晰简单。

一个简单的选择是使用SciTE (http://www.scintilla.org/SciTEDownload.html)这样的编辑器编辑java代码,它允许您对文本进行WRAP,以便容易地查看和编辑长字符串。如果你需要转义字符,你只需输入它们。通过关闭“换行”选项,您可以检查字符串是否仍然是一个很长的单行字符串。当然,如果不是,编译器也会告诉你。

Eclipse或NetBeans是否支持编辑器中的文本包装,我不知道,因为它们有太多的选项。但如果没有,这将是一个很好的补充。

这是您应该从来没有使用而不考虑它在做什么的东西。但对于一次性脚本,我已经成功地使用了这个方法:

例子:

    System.out.println(S(/*
This is a CRAZY " ' ' " multiline string with all sorts of strange
characters!
*/));

代码:

// From: http://blog.efftinge.de/2008/10/multi-line-string-literals-in-java.html
// Takes a comment (/**/) and turns everything inside the comment to a string that is returned from S()
public static String S() {
StackTraceElement element = new RuntimeException().getStackTrace()[1];
String name = element.getClassName().replace('.', '/') + ".java";
StringBuilder sb = new StringBuilder();
String line = null;
InputStream in = classLoader.getResourceAsStream(name);
String s = convertStreamToString(in, element.getLineNumber());
return s.substring(s.indexOf("/*")+2, s.indexOf("*/"));
}


// From http://www.kodejava.org/examples/266.html
private static String convertStreamToString(InputStream is, int lineNum) {
/*
* To convert the InputStream to String we use the BufferedReader.readLine()
* method. We iterate until the BufferedReader return null which means
* there's no more data to read. Each line will appended to a StringBuilder
* and returned as String.
*/
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuilder sb = new StringBuilder();


String line = null; int i = 1;
try {
while ((line = reader.readLine()) != null) {
if (i++ >= lineNum) {
sb.append(line + "\n");
}
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}


return sb.toString();
}

另一个我还没有看到的答案是java.io.PrintWriter

StringWriter stringWriter = new StringWriter();
PrintWriter writer = new PrintWriter(stringWriter);
writer.println("It was the best of times, it was the worst of times");
writer.println("it was the age of wisdom, it was the age of foolishness,");
writer.println("it was the epoch of belief, it was the epoch of incredulity,");
writer.println("it was the season of Light, it was the season of Darkness,");
writer.println("it was the spring of hope, it was the winter of despair,");
writer.println("we had everything before us, we had nothing before us");
String string = stringWriter.toString();

此外,java.io.BufferedWriter有一个newLine()方法的事实也没有被提及。

当我读到多行语法确实是为jdk7计划的(在java存在了多少年之后?)时,我有点恼火。有趣的是,甚至还没有readAll()函数用于读取文件的完整内容(仅从jdk7读取,啊啊),因此下面的代码读取单行。

/*
MakeMultiline v1.0 (2010) - Free to use and copy.


Small gadget to turn text blobs into one java string literal
(doing the split in lines, adding \n at each end and enclosing
in double quotes). Does escape quotes encountered in the text blob.


Useful for working around missing multiline string syntax in java
prior jdk7. Use with:


java MakeMultiline "    "
or
java MakeMultiline "    " mytextfile.txt
*/


import java.io.*;


class MakeMultiline {
public static void main(String[] args) {
try {
// args[0]: indent
// args[1]: filename to read (optional; stdin if not given)
// Beware the nmb of newlines at the end when using stdin!


String indent = (args.length > 0 ? args[0] : "");
FileReader fr = null; BufferedReader br;
if (args.length > 1)
{ fr =  new FileReader(args[1]); br = new BufferedReader(fr); }
else
{ br = new BufferedReader(new InputStreamReader(System.in)); }
String s; String res = "";
while((s = br.readLine()) != null) {
if (res.length() > 0) res += " +\n";
res += indent + "\"" + s.replace("\"", "\\\"") + "\\n\"";
}
br.close(); if (fr != null) fr.close();
System.out.println(res + ";");
}
catch(Exception e) {
System.out.println("Exception: " + e);
}
}
}

这对我来说是最快的解决办法。(2010-01-27)

我建议使用ThomasP建议的实用程序,然后将其链接到您的构建过程中。仍然存在一个外部文件以包含文本,但在运行时不读取该文件。 然后工作流是:

  1. 构建一个“textfile to java code”实用工具&检查版本控制
  2. 在每次构建时,针对资源文件运行实用程序以创建修改后的java源
  3. Java源代码包含类似于 class TextBlock {...后面跟着一个静态字符串,它是从资源文件
  4. 自动生成的
  5. 用剩下的代码构建生成的java文件

你可以使用scala-code,它与java兼容,并且允许用""" "括起来的多行字符串:

package foobar
object SWrap {
def bar = """John said: "This is
a test
a bloody test,
my dear." and closed the door."""
}

(注意字符串内的引号)和来自java:

String s2 = foobar.SWrap.bar ();

这样是否更舒服?

另一种方法,如果你经常处理长文本,应该放在你的源代码中,可能是一个脚本,它从外部文件中获取文本,并将其包装为一个多行java- string,像这样:

sed '1s/^/String s = \"/;2,$s/^/\t+ "/;2,$s/$/"/' file > file.java

这样你就可以很容易地复制粘贴到你的源代码中。

我至少看到了一种应该避免使用外部文件处理长字符串的情况:如果这些长字符串是单元测试文件中的预期值,因为我认为测试应该始终以一种不依赖任何外部资源的方式编写。

一个小技巧。使用这个,我在动态创建的HTML页面中注入javascript

StringBuilder builder = new StringBuilder();


public String getString()
{
return builder.toString();
}
private DropdownContent _(String a)
{
builder.append(a);
return this;
}


public String funct_showhide()
{
return
_("function slidedown_showHide(boxId)").
_("{").
_("if(!slidedown_direction[boxId])slidedown_direction[boxId] = 1;").
_("if(!slideDownInitHeight[boxId])slideDownInitHeight[boxId] = 0;").
_("if(slideDownInitHeight[boxId]==0)slidedown_direction[boxId]=slidedownSpeed; ").
_("else slidedown_direction[boxId] = slidedownSpeed*-1;").
_("slidedownContentBox = document.getElementById(boxId);").
_("var subDivs = slidedownContentBox.getElementsByTagName('DIV');").
_("for(var no=0;no<subDivs.length;no++){").
_(" if(subDivs[no].className=='dhtmlgoodies_content')slidedownContent = subDivs[no];").
_("}").
_("contentHeight = slidedownContent.offsetHeight;").
_("slidedownContentBox.style.visibility='visible';").
_("slidedownActive = true;").
_("slidedown_showHide_start(slidedownContentBox,slidedownContent);").
_("}").getString();


}

由于Java(还)不支持多行字符串,目前唯一的方法是使用前面提到的技术之一来破解它。我使用上面提到的一些技巧构建了下面的Python脚本:

import sys
import string
import os


print 'new String('
for line in sys.stdin:
one = string.replace(line, '"', '\\"').rstrip(os.linesep)
print '  + "' + one + ' "'
print ')'

把它放在一个名为javastringify.py的文件中,把你的字符串放在一个名为mystring.txt的文件中,然后像下面这样运行:

cat mystring.txt | python javastringify.py

然后,您可以复制输出并将其粘贴到编辑器中。

修改这需要处理任何特殊情况,但这是为我的需要。希望这能有所帮助!

在Eclipse中,如果你打开选项“当粘贴到字符串文字时转义文本”(在Preferences > Java > Editor > Typing中)并在引号中粘贴一个多行字符串,它将自动为所有行添加"\n" +

String str = "paste your text here";

看到# EYZ0。如果需要,将文本转换为StringBuilder java块转义。

这看起来可能有点疯狂,但由于这里文档是单行声明的语法糖,并转义了换行符,因此可以为Java文件编写预处理程序,在预处理期间将这里文档更改为单行。

这需要在编译阶段(对于ant/maven构建)之前为预处理文件编写适当的插件,并为IDE编写插件。

从意识形态的观点来看,它与f.g.没有什么不同。“泛型”,这也是一种预处理的语法加糖铸造。

然而,这是一个大量的工作,所以我会在你的地方只使用.properties文件。

如果你像我一样喜欢谷歌的番石榴,它可以提供一个相当干净的表示和一个很好的,简单的方法来不硬编码你的换行符:

String out = Joiner.on(newline).join(ImmutableList.of(
"line1",
"line2",
"line3"));

这是一个老线程,但是一个新的非常优雅的解决方案(只有4个或3个小缺点)是使用自定义注释。

检查:http://www.adrianwalker.org/2011/12/java-multiline-string.html

受此启发的一个项目托管在GitHub上:

# EYZ0

Java代码示例:

import org.adrianwalker.multilinestring.Multiline;
...
public final class MultilineStringUsage {


/**
<html>
<head/>
<body>
<p>
Hello<br/>
Multiline<br/>
World<br/>
</p>
</body>
</html>
*/
@Multiline
private static String html;


public static void main(final String[] args) {
System.out.println(html);
}
}

缺点是

  1. 则必须激活相应的(提供的)注释 处理器。李< / >
  2. 检查原始字符串字面量项目,你可以将变量定义为局部变量
  3. 表示字符串不能包含Visual Basic .Net中的其他变量 使用XML literal (<%= variable %>):-)
  4. 字符串文字由JavaDoc注释(/**)分隔

而且您可能必须配置Eclipse/Intellij-Idea,使其不会自动重新格式化Javadoc注释。

有人可能会觉得这很奇怪(Javadoc注释并不是为嵌入注释以外的任何内容而设计的),但由于Java中缺少多行字符串确实令人讨厌,因此我认为这是最不糟糕的解决方案。

后期模型JAVA对+和常量字符串进行了优化,在幕后使用了StringBuffer,所以你不想让它使你的代码变得混乱。

它指出了JAVA的一个疏忽,它不像ANSI C在双引号字符串之间只有空白的自动连接,例如:

const char usage = "\n"
"Usage: xxxx <options>\n"
"\n"
"Removes your options as designated by the required parameter <options>,\n"
"which must be one of the following strings:\n"
"  love\n"
"  sex\n"
"  drugs\n"
"  rockandroll\n"
"\n" ;

我想有一个多行字符数组常量,其中嵌入换行是光荣的,所以我可以在没有任何混乱的情况下呈现块,例如:

String Query = "
SELECT
some_column,
another column
FROM
one_table a
JOIN
another_table b
ON    a.id = b.id
AND a.role_code = b.role_code
WHERE a.dept = 'sales'
AND b.sales_quote > 1000
Order BY 1, 2
" ;

要做到这一点,需要打败JAVA之神。

使用# EYZ0。不需要外部库。

比混乱的代码好(因为可维护性和设计是你所关心的),最好不要使用长字符串。

首先读取xml属性:

 InputStream fileIS = YourClass.class.getResourceAsStream("MultiLine.xml");
Properties prop = new Properies();
prop.loadFromXML(fileIS);
< p >
然后你可以使用你的多行字符串在更易于维护方式…

static final String UNIQUE_MEANINGFUL_KEY = "Super Duper UNIQUE Key";
prop.getProperty(UNIQUE_MEANINGFUL_KEY) // "\n    MEGA\n   LONG\n..."
< p >
MultiLine.xml`位于同一个文件夹YourClass:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">


<properties>
<entry key="Super Duper UNIQUE Key">
MEGA
LONG
MULTILINE
</entry>
</properties>

注:你可以使用<![CDATA[""]]>用于类似xml的字符串。

我有时使用一个并行groovy类来充当一个字符串包

这里的java类

public class Test {
public static void main(String[] args) {
System.out.println(TestStrings.json1);
// consume .. parse json
}
}

以及TestStrings.groovy中令人垂涎的多行字符串

class TestStrings {
public static String json1 = """
{
"name": "Fakeer's Json",
"age":100,
"messages":["msg 1","msg 2","msg 3"]
}""";
}

当然,这只适用于静态字符串。如果我必须在文本中插入变量,我会将整个文件更改为groovy。只要保持强类型实践,它就可以实现。

    import org.apache.commons.lang3.StringUtils;


String multiline = StringUtils.join(new String[] {
"It was the best of times, it was the worst of times ",
"it was the age of wisdom, it was the age of foolishness",
"it was the epoch of belief, it was the epoch of incredulity",
"it was the season of Light, it was the season of Darkness",
"it was the spring of hope, it was the winter of despair",
"we had everything before us, we had nothing before us",
}, "\n");

实际上,下面是我迄今为止见过的最干净的实现。它使用注释将注释转换为字符串变量…

/**
<html>
<head/>
<body>
<p>
Hello<br/>
Multiline<br/>
World<br/>
</p>
</body>
</html>
*/
@Multiline
private static String html;

因此,最终结果是变量html包含多行字符串。没有引号,没有加号,没有逗号,只有纯字符串。

此解决方案可从以下URL获得… # EYZ0 < / p >

希望有帮助!

从问题中还不完全清楚作者是否有兴趣使用某种需要有一些动态值的格式化大字符串,但如果是这种情况,像StringTemplate (http://www.stringtemplate.org/)这样的模板引擎可能非常有用。

下面是一个使用StringTemplate的简单代码示例。实际的模板(“Hello, <Name >")可以从外部纯文本文件加载。模板中的所有缩进都将被保留,不需要转义。

import org.stringtemplate.v4.*;
 
public class Hello {
    public static void main(String[] args) {
        ST hello = new ST("Hello, <name>");
        hello.add("name", "World");
        System.out.println(hello.render());
    }
}

附注:为了可读性和本地化目的,从源代码中删除大块文本总是一个好主意。

一个不错的选择。

import static some.Util.*;


public class Java {


public static void main(String[] args) {


String sql = $(
"Select * from java",
"join some on ",
"group by"
);


System.out.println(sql);
}


}




public class Util {


public static String $(String ...sql){
return String.join(System.getProperty("line.separator"),sql);
}


}

在IntelliJ IDE中,你只需要输入:

""

然后将光标放置在引号内并粘贴字符串。IDE将它扩展为多个连接的行。

JEP 378:文本块涵盖了这个功能,并且包含在JDK 15中。它首先出现在JDK 13中的JEP 355:文本块(预览)和JDK 14中的JEP 368:文本块(第二次预览),并且可以在这些版本中使用––enable–preview javac选项启用。

语法允许这样写:

String s = """
text
text
text
""";

在此JEP之前,在JDK 12中,JEP 326:原始字符串文字旨在实现类似的功能,但最终被撤回:

请注意:这原本是JDK 12中的预览语言特性,但它已被撤回,并没有出现在JDK 12中。它在JDK 13中被Text Blocks (JEP 355)所取代。

我知道这是一个老问题,但是对于感兴趣的开发人员来说,多行文字将在#Java12中

http://mail.openjdk.java.net/pipermail/amber-dev/2018-July/003254.html

使用JDK/12早期访问版本# 12,现在可以在Java中使用多行字符串,如下所示:

String multiLine = `First line
Second line with indentation
Third line
and so on...`; // the formatting as desired
System.out.println(multiLine);

这将导致以下输出:

First line
Second line with indentation
Third line
and so on...
< p >编辑: 延迟到java 13

使用这个库

https://github.com/alessio-santacroce/multiline-string-literals

可以这样写

System.out.println(newString(/*
Wow, we finally have
multiline strings in
Java! HOOO!
*/));

很好很简单,但是只适用于单元测试

Java 13预览:

文本块来Java。 Java 13提供了期待已久的多行字符串Mala Gupta

通过文本块,Java 13使您更容易处理多行字符串字面量。您不再需要转义字符串字面量中的特殊字符,也不再需要对跨多行的值使用连接操作符。

文本块是使用三个双引号(""")作为开始和结束分隔符定义的。开始分隔符后面可以跟着零个或多个空格和行结束符。

例子:

 String s1 = """
text
text
text
""";

这个问题有两个答案:

  1. 如果你想坚持纯Java,随着Java 14将于2020年3月发布,你可以在第二预览模式下利用中368 -文本块。实际上,该功能在其他版本中处于预览模式(至少有13个版本)。我创建了一个示例集在这里
  2. 虽然这个特性很有用,但是它可以是很容易滥用。请记住,Java需要编译——在代码中使用大字符数组很容易伤到自己的腿(如果您想快速更改,则需要重新编译——操作应用程序的人员可能无法使用该工具集)。

根据我的经验,建议在配置文件中保留大字符串(通常是应用程序操作员可以/应该在运行时更改的字符串)。

总结:负责地使用文本块:)。

Java 13及以上版本

Java现在通过文本块支持多行字符串。在Java 13和14中,这个特性要求您在构建和运行项目时设置––enable–preview选项。在Java 15及以后版本中,由于文本块已成为标准特性,因此不再需要此选项。查看官方文本块的程序员指南了解更多细节。

现在,在Java 13之前,你是这样写查询的:

List<Tuple> posts = entityManager
.createNativeQuery(
"SELECT *\n" +
"FROM (\n" +
"    SELECT *,\n" +
"           dense_rank() OVER (\n" +
"               ORDER BY \"p.created_on\", \"p.id\"\n" +
"           ) rank\n" +
"    FROM (\n" +
"        SELECT p.id AS \"p.id\",\n" +
"               p.created_on AS \"p.created_on\",\n" +
"               p.title AS \"p.title\",\n" +
"               pc.id as \"pc.id\",\n" +
"               pc.created_on AS \"pc.created_on\",\n" +
"               pc.review AS \"pc.review\",\n" +
"               pc.post_id AS \"pc.post_id\"\n" +
"        FROM post p\n" +
"        LEFT JOIN post_comment pc ON p.id = pc.post_id\n" +
"        WHERE p.title LIKE :titlePattern\n" +
"        ORDER BY p.created_on\n" +
"    ) p_pc\n" +
") p_pc_r\n" +
"WHERE p_pc_r.rank <= :rank\n",
Tuple.class)
.setParameter("titlePattern", "High-Performance Java Persistence %")
.setParameter("rank", 5)
.getResultList();

多亏了Java 13 Text Blocks,你可以像下面这样重写这个查询:

List<Tuple> posts = entityManager
.createNativeQuery("""
SELECT *
FROM (
SELECT *,
dense_rank() OVER (
ORDER BY "p.created_on", "p.id"
) rank
FROM (
SELECT p.id AS "p.id",
p.created_on AS "p.created_on",
p.title AS "p.title",
pc.id as "pc.id",
pc.created_on AS "pc.created_on",
pc.review AS "pc.review",
pc.post_id AS "pc.post_id"
FROM post p
LEFT JOIN post_comment pc ON p.id = pc.post_id
WHERE p.title LIKE :titlePattern
ORDER BY p.created_on
) p_pc
) p_pc_r
WHERE p_pc_r.rank <= :rank
""",
Tuple.class)
.setParameter("titlePattern", "High-Performance Java Persistence %")
.setParameter("rank", 5)
.getResultList();

可读性强多了,对吧?

IDE支持

IntelliJ IDEA提供了将遗留的String连接块转换为新的多行String格式的支持:

IntelliJ IDEA Text Blocks support

Json, html, XML

多行代码String在编写JSON、HTML或XML时特别有用。

考虑这个例子,使用String连接来构建一个JSON字符串文字:

entityManager.persist(
new Book()
.setId(1L)
.setIsbn("978-9730228236")
.setProperties(
"{" +
"   \"title\": \"High-Performance Java Persistence\"," +
"   \"author\": \"Vlad Mihalcea\"," +
"   \"publisher\": \"Amazon\"," +
"   \"price\": 44.99," +
"   \"reviews\": [" +
"       {" +
"           \"reviewer\": \"Cristiano\", " +
"           \"review\": \"Excellent book to understand Java Persistence\", " +
"           \"date\": \"2017-11-14\", " +
"           \"rating\": 5" +
"       }," +
"       {" +
"           \"reviewer\": \"T.W\", " +
"           \"review\": \"The best JPA ORM book out there\", " +
"           \"date\": \"2019-01-27\", " +
"           \"rating\": 5" +
"       }," +
"       {" +
"           \"reviewer\": \"Shaikh\", " +
"           \"review\": \"The most informative book\", " +
"           \"date\": \"2016-12-24\", " +
"           \"rating\": 4" +
"       }" +
"   ]" +
"}"
)
);

由于转义字符和大量的双引号和加号,您几乎无法阅读JSON。

使用Java文本块,JSON对象可以这样写:

entityManager.persist(
new Book()
.setId(1L)
.setIsbn("978-9730228236")
.setProperties("""
{
"title": "High-Performance Java Persistence",
"author": "Vlad Mihalcea",
"publisher": "Amazon",
"price": 44.99,
"reviews": [
{
"reviewer": "Cristiano",
"review": "Excellent book to understand Java Persistence",
"date": "2017-11-14",
"rating": 5
},
{
"reviewer": "T.W",
"review": "The best JPA ORM book out there",
"date": "2019-01-27",
"rating": 5
},
{
"reviewer": "Shaikh",
"review": "The most informative book",
"date": "2016-12-24",
"rating": 4
}
]
}
"""
)
);

自从我在2004年使用c#以来,我一直希望在Java中有这个功能,现在我们终于有了它。

Java15现在像Python一样支持三引号字符串。

看到所有的答案,我认为没有人提到比在java的新版本,你可以这样做:

String s = """
This
is
a
multiline
string
""";
System.out.println(s);

这是它打印的内容:

This
is
a
multiline
string