Java多行字符串


516

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

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

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

有哪些更好的选择?在属性文件中定义我的字符串?

编辑:两个答案说StringBuilder.append()比加号法更可取。任何人都可以解释为什么会这样吗?它对我来说似乎并不更可取。我正在寻找一种解决办法,即多行字符串不是一流的语言构造,这意味着我绝对不希望用方法调用替换一流的语言构造(带有plus的字符串连接)。

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


12
重复添加到字符串时,StringBuilder.append()比plus更可取,因为每次string1 + string2您分配新的字符串对象并从两个输入字符串中复制字符时,StringBuilder.append()都比plus更可取。如果将n个字符串加在一起,则将进行n-1个分配和大约(n ^ 2)/ 2个字符的复制。另一方面,StringBuilder的复制和重新分配的频率降低了(尽管当您超出其内部缓冲区的大小时,它仍然会进行复制和重新分配)。从理论上讲,在某些情况下,编译器可以将+转换为使用StringBuilder,但实际上是谁知道的。
劳伦斯·贡萨尔维斯

5
每次我跳入调试器时,在Java 1.5上,+都会转换为StringBuilder.append()调用。我曾经让同事困惑地告诉我,StringBuilder有一个错误,因为他们调试了似乎没有调用它的代码,并在那里结束。
skiphoppy


61
请注意,由“ abc \ n” +“ def \ n”等组成的字符串文字不使用StringBuilder:编译器将它们粘合在一起,并将它们作为单个文字放入.class文件中,与其他类型的不断折叠。
araqnid

6
大多数IDE支持输入多行字符串。即。您只需在“”字符串中键入或粘贴所需内容,它将根据需要添加\ n和“ +”。例如,我可以将40行文本粘贴到字符串中,然后IDE会为您整理出来。
彼得·劳瑞

Answers:


118

Stephen Colebourne 提出了在Java 7中添加多行字符串的建议

另外,Groovy已经支持多行字符串


14
用于增强Java的Project Coin流程包括多行字符串mail.openjdk.java.net/pipermail/coin-dev/2009-February/…。它被Oracle blogs.sun.com/darcy/entry/project_coin_final_five拒绝。
JodaStephen 2011年

8
2012年有什么变化吗?
伊利亚·G 2012年

13
不幸的是,这似乎尚未纳入规范。
namuol

3
blogs.sun.com链接已损坏,但我认为内容现在位于blogs.oracle.com/darcy/entry/project_coin_final_five
Donal Fellows

8
似乎,截至2018年1月,社区正在重新考虑多行字符串。openjdk.java.net/jeps/326
Shane Gannon

485

听起来您想执行多行文字,这在Java中是不存在的。

最好的选择是将字符串+组合在一起。人们提到的其他一些选项(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";

StringBuilder

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();

String.format()

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(),也可以%n在中使用String.format

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


246
此外,由于所有字符串在编译时都是已知的,因此第一个版本将由编译器自动连接。即使在编译时不知道字符串,它也不会比StringBuilder或String.format()慢。避免与+串联的唯一原因是在循环中进行。
迈克尔·迈尔斯

23
String.format版本的问题在于您必须使格式与行数保持同步。
巴特·范·海克洛姆

4
与其他两个示例相比,String.format效率不高
cmcginty 2010年

10
这个答案对于眼前的问题是非常不合适的解决方案。我们有2000行SAS宏或200行SQL查询串,我们希望将其复制和粘贴。建议我们使用+“” concat将这些多行文本转换为StringBuffer追加是荒谬的。
Blessed Geek

21
@BlessedGeek:当前的问题是关于Java语言中可用的选项。它没有提及有关输入字符串的数据类型的任何信息。如果有更好的解决方案,则可以将其发布为答案。听起来Josh Curren的解决方案会更适合您的情况。如果您不满意该语言不支持多行文字,那么这是抱怨的地方不对。
Kip 2012年

188

在Eclipse中,如果打开“粘贴到字符串文字中时转义文本”选项(在“首选项”>“ Java”>“编辑器”>“键入”中)并粘贴带有引号的多行字符串,它将为所有行自动添加"\n" +

String str = "paste your text here";

15
默认情况下,当您粘贴到“” s中时,intelij也会执行此操作
Bob B

您是否通常将\rEclipse放在Windows上?
Noumenon

99

这是一个旧线程,但是一个新的相当优雅的解决方案(只有4个也许有3个小缺陷)是使用自定义注释。

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

受此工作启发的项目托管在GitHub上:

https://github.com/benelog/multiline

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. 不能将String变量定义为局部变量检查Raw String Literals项目,您可以在其中将变量定义为局部变量
  3. 该字符串不能包含其他变量,如Visual Basic .Net中带有XML文字(<%= variable %>)的:-)
  4. 字符串文字由JavaDoc注释(/ **)分隔

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

可能会发现这很奇怪(Javadoc注释除了嵌入注释之外,没有设计嵌入其他任何东西),但是由于Java最终缺少多行字符串确实很烦人,因此我认为这是最糟糕的解决方案。


这是否要求使用多行字符串的类是最终的?另外,从Eclipse开发和执行代码时是否需要任何设置?参考URL提到了Maven进行注释处理的设置要求。我无法弄清楚可能需要什么,如果在Eclipse中需要的话。
大卫,

注释是可行的-但似乎对Maven也存在严格的依赖吗?这部分剥夺了Heredoc的许多价值,这些价值是简化小文本的管理。
javadba

3
您可以完全在Eclipse中完成此操作。上面@SRG发布的链接将您指向此链接。如果您使用的是eclipse,则需要一分钟的设置,并且可以正常工作。
Michael Plautz 2014年

4
这可能是我见过的最大的黑客事件。编辑:没关系...参见鲍勃·奥尔布赖特(Bob Albrights)的答案。
Llew Vallis '18

1
我对该项目进行了扩展,并创建了一个支持局部变量的新项目,请看一下该项目
deFreitas

62

另一种选择是将长字符串存储在外部文件中,然后将文件读取为字符串。


13
究竟。大量文本不属于Java源代码;而是使用适当格式的资源文件,该文件通过调用Class.getResource(String)加载。
erickson

5
对!您还可以使用Locale + ResourceBundle来轻松加载I18N文本,然后String.format()调用会将“ \ n”解析为换行符:)示例:String readyStr = String.parse(resourceBundle.getString(“介绍”));
ATorras

62
您不必仅仅因为String是多行而就将其外部化。如果我有一个正则表达式想要与注释分成多行怎么办?在Java中看起来很难看。@C#的语法更加简洁。
杰里米·斯坦

8
Skiphoppy不想只使用段落长度字符串常量来处理文件的开销。我一直在C ++中使用多行字符串,这些字符串嵌入我想要的源代码中。
蒂姆·库珀

9
哇。我不能相信C ++在这个问题上实际上比Java好!我喜欢多行字符串常量,在某些情况下它们确实属于源代码。
User1

59

这是您在不考虑它在做什么的情况下永远不应该使用的东西。但是对于一次性脚本,我已经成功地使用了它:

例:

    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();
}

15
需要附带最终二进制文件的类的Java代码。嗯
托尔比约恩Ravn的安徒生

23
我可以想象我的同事在尝试检查类似情况时的反应……
Landon Kuhn 2012年

15
+1。有些人缺乏想象力,却投了反对票。这是用于编写小型实用程序,测试用例甚至在受控产品环境中的有用构造。这是将Java退出ruby / python / etc还是留在这里的一个区别。
javadba

1
很好的解决方案,但不幸的是,它不适用于android,因为它将在模拟器或真实设备上执行,并且那里没有源代码。
evgeny.myasishchev 2015年

也许它是Java 8探针,还是其他,但是示例中的classLoader不存在。我尝试使用MyClass.class.getResourceAsStream(...),但它始终返回null。将会是一个很棒的快速测试解决方案!
尼克,

54

String.join

Java 8添加了一个新的静态方法,java.lang.String该方法提供了更好的选择:

String.join( CharSequence delimiter , CharSequence... elements )

使用它:

String s = String.join(
    System.getProperty("line.separator"),
    "First line.",
    "Second line.",
    "The rest.",
    "And the last!"
);

5
干净整洁的解决方案!不依赖于IDE和预处理器!无需任何手册"\n",并且知道可移植性!
小清庞-明日香健治-'Dec

1
我知道我的评论是没有用的,但是为了寻求诸如多行字符串文字之类的基本东西而遭到黑客的抨击却是如此。为何Java仍无法将其添加到规范中?
德米特里(Dmitry)'18 -10-9


19

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

string:text\u000atext\u000atext\u000a

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

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

如果确实有新行,则可能需要一些连接或格式化方法:

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

17

加号将转换为StringBuilder.append,除非两个字符串都是常量,以便编译器可以在编译时将它们合并。至少,这就是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";

就是说,将字符串文字分成多行并带有加号以提高可读性没有任何惩罚。


4
从技术上讲,在您的第一个示例中,它生成的内容类似于:String c = new StringBuilder()。append(a).append(b).toString(); 区别在于,临时字符串生成器超出范围,并且可以在String c = ...行之后立即进行垃圾回收,而“ temp”字符串生成器将保留更长的时间。
Kip

真正。我的观点当然是要区分何时在运行时调用函数与何时可以在编译时完成工作。但是你是对的。
杰伊,2009年

15

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

""

然后将光标置于引号内并粘贴字符串。IDE会将其扩展为多个串联的行。


11

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

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

假设您正在上一个名为Foo的课程。只是做这样的事情:

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

另一个烦人的地方是Java没有标准的“将所有文本从此Reader读入String”方法。虽然很容易写:

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();
}

我也一样。您可以使用commons-io来更轻松地读取文件的内容(使用“ FileUtils.readFileToString(File file)”)。
SRG 2012年

关于Java的说法
ccpizza

10
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);

我的意见是,它删除了加号和引号,使其更具可读性,尤其是在多于3行的情况下。虽然不如String.format好。
汤姆,

2
Stringbuilder示例至少是不可读的。另外,请不要忘记“ \ n”并不总是换行符,但是对于Linux和UNIX机器都很好。
Stefan Thyberg,2009年

另外,只想提及StringBuilder的存在。
汤姆,

4
用六个字符的方法名称和括号替换一个加号对我来说似乎不太可读,尽管显然您并不是唯一这样认为的人。但是,它不会删除引号。他们仍然在那里。
skiphoppy

9

由于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

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

根据需要修改此名称以处理任何特殊情况,但这可以满足我的需求。希望这可以帮助!


9

您可以使用与Java兼容的scala代码,并允许使用“””括起来的多行字符串:

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 ();

这是否更舒适...?

如果您经常处理应放在源代码中的长文本,另一种方法可能是脚本,该脚本从外部文件获取文本,并将其包装为multiline-java-String,如下所示:

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

这样您就可以轻松地将其剪切并粘贴到您的源文件中。


8

您可以使用类似的单独方法连接您的追加:

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

无论哪种方式,都倾向于StringBuilder使用加号。


5
为什么我更喜欢StringBuilder而不是加号?
skiphoppy

10
效率,或者通常是误导性的尝试。
迈克尔·迈尔斯

2
我认为,提高效率的尝试基于Java编译器使用StringBuilder(1.5之前的编译器中的StringBuffer)实现字符串连接运算符的事实。有一篇古老而著名的文章指出,在某些情况下使用StringBuffer(或现在的StringBuilder)可以提高性能。这是链接:java.sun.com/developer/JDCTechTips/2002/tt0305.html
Paul Morie 09年

4
仅当编译器无法执行此操作时。对于文字和常量,如果使用加号,则级联在编译时完成。使用StringBuilder会强制它在运行时发生,因此不仅工作更多,而且速度更慢。
johncip

7

实际上,以下是我到目前为止看到的最干净的实现。它使用注释将注释转换为字符串变量...

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

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

可从以下URL获得此解决方案... http://www.adrianwalker.org/2011/12/java-multiline-string.html

希望有帮助!


该注释处理器需要更强大的检查,
Tripp Kinetics

我喜欢这个。想出来
javadba

7

参见Java Stringfier。如果需要,将文本转换为转义的StringBuilder Java块。


1
是的,因为我可以一辈子复制并粘贴到该站点中。我也可以将它们存储在文件中并加载该文件,但这也不是理想的解决方案。
毫米

7
    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");

6

我尚未看到的替代方法是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()方法的事实。


5

如果您像我一样喜欢Google的番石榴,它可以提供一种非常简洁的表示形式,并且是一种不对换行符也进行硬编码的简便方法:

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

5

使用Properties.loadFromXML(InputStream)。不需要外部库。

比凌乱的代码更好(因为您需要关注可维护性和设计),因此最好不要使用长字符串。

首先阅读xml属性:

 InputStream fileIS = YourClass.class.getResourceAsStream("MultiLine.xml");
 Properties prop = new Properies();
 prop.loadFromXML(fileIS);


那么您可以以更易于维护的方式使用多行字符串...

static final String UNIQUE_MEANINGFUL_KEY = "Super Duper UNIQUE Key";
prop.getProperty(UNIQUE_MEANINGFUL_KEY) // "\n    MEGA\n   LONG\n..."


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>

PS .:您可以将<![CDATA["... "]]>用于类似xml的字符串。


是的,这也是我使用的,很好的解决方案!将SQL或XML移到外部XML属性文件中。它不会弄乱代码。:)
Laszlo Lugosi

这不能回答问题。按定义,heredoc 位于文件中。关键是将其放在一个位置。
javadba

5

使用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...

编辑:推迟到Java 13


1
这是一种使用Maven
-Naman

2
正如Cyber​​soft在其他评论中所说的那样,原始字符串文字(JEP326)已从最终JDK12中删除,但已创建另一个JEP以添加“文本块”,可以在JDK 13中进行预览
Manuel Romeiro

4

一个非常有效且与平台无关的解决方案是将system属性用于行分隔符,并使用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();

4

一个不错的选择。

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);
        }

    }

4

这是一个非常常见的问题,所以我决定也将这个答案变成一篇文章

Java 13及更高版本

Java现在通过文本块支持多行字符串。在Java 13和14中,此功能要求您––enable–preview在构建和运行项目时设置选项。查看此Java文档以获取更多详细信息。

现在,在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文本块,您可以按以下方式重写此查询:

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文本块支持

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中具有此功能,现在终于有了它。


3

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

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


属性文件中的值可以跨越多行:仅将所有行(最后一行除外)用反斜杠结尾。确实会留下用作行分隔符的问题,因为这是特定于平台的。我想您可以使用一个简单的\ n,然后在代码中读取该属性后,将\ n进行搜索和替换为line.separator。这似乎有点麻烦,但是我想您可以编写一个函数来检索属性并同时执行此操作。好吧,所有这些假设都将您将这些字符串写入文件中,这是一个很大的假设。
杰伊,


3

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

  1. 构建“文本文件到Java代码”实用程序并检入版本控制
  2. 在每个构建中,对资源文件运行实用程序以创建修订的Java源
  3. Java源代码包含一个标头, class TextBlock {...其后是一个静态字符串,该静态字符串是从资源文件自动生成的
  4. 使用其余代码构建生成的Java文件

2

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

只有使用多个语句来构造String时,StringBuilder才更有效率。

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

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

使其尽可能简单明了。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.