混淆:字符串是不可变的,所以不要用于连接

时间:2015-03-25 14:38:15

标签: java string concatenation

JAVA:我想清楚使用像这样的字符串

String str = " SELECT "
           + "field1, "
           + "field2, "
           + "field3  "
           + " FROM table1; ";

以这种方式使用。

根据字符串的最佳做法询问,不要用于连接

这是非常短的查询,但实际上我们有非常非常大的查询。这只是一个例子。所以我只想确认我没有做任何错误。

6 个答案:

答案 0 :(得分:3)

String+运算符的连接在某些上下文中可能被视为不良做法,即如果在编译时它不能被StringBuilder调用自动替换。

一般情况下,如果您的字段是常量或只是一次初始化,则String+的连接完全没问题。

但是,如果您正在动态构建String并循环/递归,那么最好使用StringBuilder(或StringBuffer进行线程安全连接)。

答案 1 :(得分:2)

这没关系,与将其写成一个字符串完全相同:

String str = " SELECT field1, field2, field3   FROM table1; ";

如果要连接字符串文字,编译器将在编译时进行连接,因此生成的字节码完全相同,无论您是像编写它还是作为一个字符串文字一样编写它。

答案 2 :(得分:1)

我非常怀疑field1和field2和table1是预先知道的,我认为它们是某个函数的参数。

在这种情况下,应该使用StringBuilder。即使你用加号连接,你仍然(几乎总是)得到一个StringBuilder,只看一下字节码。这是一个参考:

  

“实现可以选择一步执行转换和连接,以避免创建然后丢弃中间String对象。为了提高重复字符串连接的性能,Java编译器可以使用StringBuffer类或类似的减少通过评估表达式创建的中间String对象数量的技术。“15.18.1

请记住,如果你要循环,会有性能损失。

答案 3 :(得分:1)

您的示例很好,因为它只涉及 literals ,编译器将在编译时进行连接。


但如果你有:

String str = " SELECT "
           + "field1, "
           + someVariable
           + "field3  "
           + " FROM table1; ";

...然后编译器只能为你处理它的一部分,在运行时产生这个:

String str = " SELECT field1, "
           + someVariable
           + "field3   FROM table1; ";

它在运行时发生的方式是通过StringBuilder,听起来很棒,但遗憾的是效率很低,实际上是:

StringBuilder sb1 = new StringBuilder();
sb1.append(" SELECT field1, ");
sb1.append(someVariable);
String temp = sb1.toString();
StringBuilder sb2 = new StringBuilder();
sb2.append(temp);
sb2.append("field3   FROM table1; ");
String str = sb2.toString();

它不仅创建多个StringBuilder实例并对toString进行不必要的调用,而且它使用默认构造函数,它只为其数组中的16个字符的字符串分配足够的空间(最后一个选中)。所以StringBuilder可能必须在至少重新分配其内部数组一次。布莱克,对吗? (等等......)

与您自己做的事情相比:

String str = (new StringBuilder(200))
                .append(" SELECT " +
                        "field1, ")
                .append(someVariable)
                .append("field3  " +
                        " FROM table1; ")
                .toString();

效率更高。

更好 ?看起来很糟糕,很难维护。从性能的角度来看,你必须在紧密的循环中做到这一点。

如果您遇到有助于解决的性能问题,那就更好了。

如果不这样做,那就不是“更好”,因为代码不太清楚。

如果你只处理文字,这些都不重要。

答案 4 :(得分:1)

当你在循环中连接字符串时,

+被认为是错误的。让我们说你有

String s = "f";
for (int i=0; i<100; i++){
    s = s + "o";
}

在这种情况下

s = s + "o";

将编译为

s = new StringBuilder(f).append("o").toString();

所以在每次迭代中你都在创建新的StringBuilder,你正在将s的当前内容复制到它,然后你要添加o并通过调用toString来重新创建新的String实例存储在s引用中。正如你所看到的那样,很多事情要做,特别是coppying和创建String的新实例,执行的时间取决于字符串的长度。

这样做的首选方法是创建自己的StringBuilder,在循环中追加你想要的东西,并在循环结束后从StringBuilder的内容创建新的String对象。像

这样的东西
StringBuilder sb = new StringBuilder("f");
for (int i=0; i<100; i++){
    sb.append("o");
}
String s = sb.toString();

但是在你的情况下,因为你在编译时常量上运行而不能改变,所以编译器能够找出

String str = " SELECT "
           + "field1, "
           + "field2, "
           + "field3  "
           + " FROM table1; ";

只能是" SELECT field1, field2, field3 FROM table1; ",所以它会在编译时连接起来,所以你的str将被编译为

String str = " SELECT field1, field2, field3   FROM table1; ";

答案 5 :(得分:1)

你在这里做的事情并没有错。但是,由于以下原因,这很尴尬:

  • 不合理:所有操作员和报价都让人讨厌阅读

  • 容易出错:修改此内容以省略子字符串中的空格并破坏查询

  • 不方便:您无法将查询复制到SQL工具中,而无需停止并删除所有运算符和引号。

您可以将这样的SQL提取到配置文件中,可以使用属性文件或XML(使用CDATA)。您可以在Spring Integration - Externalizing JDBC Queries

查看示例