Java 在SQL中使用StringBuilder的正确方法

Java 在SQL中使用StringBuilder的正确方法,java,string,stringbuilder,Java,String,Stringbuilder,我刚刚在我的项目中发现了一些类似这样的sql查询构建: return (new StringBuilder("select id1, " + " id2 " + " from " + " table")).toString(); 这StringBuilder是否实现了其目标,即减少内存使用 我对此表示怀疑,因为构造函数中使用了“+”(字符串concat运算符)。这会占用与使用下面代码中的字符串相同的内存量吗?据我所知,使用StringBuilder.append()时会有所不同 这两个语句在内

我刚刚在我的项目中发现了一些类似这样的sql查询构建:

return (new StringBuilder("select id1, " + " id2 " + " from " + " table")).toString();
StringBuilder
是否实现了其目标,即减少内存使用

我对此表示怀疑,因为构造函数中使用了“+”(字符串concat运算符)。这会占用与使用下面代码中的字符串相同的内存量吗?据我所知,使用
StringBuilder.append()
时会有所不同

这两个语句在内存使用方面是否相等?请澄清

提前谢谢

编辑:

顺便说一句,这不是我的代码。在一个老项目中找到的。此外,该查询并不像我的示例中的查询那么小。:)

使用StringBuilder的目的是减少内存。它实现了吗

不,一点也不。该代码未正确使用
StringBuilder
。(不过,我认为你引用错了;
id2
table
?)

请注意,其目的(通常)是减少内存流失,而不是减少使用的内存总量,从而使垃圾收集器的工作更轻松

这会占用与使用如下字符串相同的内存吗

不,这会造成更多的内存流失,而不仅仅是你引用的直接concat。(直到/除非JVM优化器发现代码中的显式
StringBuilder
是不必要的,并尽可能对其进行优化。)

如果该代码的作者想要使用
StringBuilder
(有支持的,也有反对的;请参阅本答案末尾的注释),最好正确地使用它(这里我假设在
id2
周围实际上没有引号):

请注意,我在
StringBuilder
构造函数中列出了一些合适的大小,这样一来,它一开始就有足够的容量来容纳我们要附加的全部内容。如果不指定默认大小,则使用的默认大小为,通常太小,导致
StringBuilder
必须重新分配以使其更大(IIRC,在Sun/Oracle JDK中,每次用完空间时,它会将自身加倍[或者更多,如果它知道需要更多来满足特定的
追加
]

您可能听说,如果使用Sun/Oracle编译器编译,字符串连接将在封面下使用
StringBuilder
。这是事实,它将使用一个
StringBuilder
作为整个表达式。但它将使用默认构造函数,这意味着在大多数情况下,它必须进行重新分配。不过,它更容易阅读。请注意,对于一系列串联,情况并非如此。例如,它使用一个
StringBuilder

return "prefix " + variable1 + " middle " + variable2 + " end";
// 1 StringBuilder used in this line
String query = "select id1, " + fieldName + " from " + tableName;
if (where != null) {
   // another StringBuilder used here
   query += ' ' + where;
}
它大致可以翻译为:

StringBuilder tmp = new StringBuilder(); // Using default 16 character size
tmp.append("prefix ");
tmp.append(variable1);
tmp.append(" middle ");
tmp.append(variable2);
tmp.append(" end");
return tmp.toString();
所以这没关系,虽然默认构造函数和随后的重新分配并不理想,但可能性是它已经足够好了,而且连接更具可读性

但这只适用于一个表达式。多个
StringBuilder
s用于此:

String s;
s = "prefix ";
s += variable1;
s += " middle ";
s += variable2;
s += " end";
return s;
最终会变成这样:

String s;
StringBuilder tmp;
s = "prefix ";
tmp = new StringBuilder();
tmp.append(s);
tmp.append(variable1);
s = tmp.toString();
tmp = new StringBuilder();
tmp.append(s);
tmp.append(" middle ");
s = tmp.toString();
tmp = new StringBuilder();
tmp.append(s);
tmp.append(variable2);
s = tmp.toString();
tmp = new StringBuilder();
tmp.append(s);
tmp.append(" end");
s = tmp.toString();
return s;
…这太难看了


但是,重要的是要记住,除了极少数情况外,其他情况都无关紧要,除非出现特定的性能问题,否则最好使用可读性(提高可维护性)。

在您发布的代码中没有优势,因为您误用了StringBuilder。在这两种情况下构建相同的字符串。使用StringBuilder,您可以使用
append
方法避免对字符串执行
+
操作。 您应该这样使用它:

return new StringBuilder("select id1, ").append(" id2 ").append(" from ").append(" table").toString();
在Java中,字符串类型是一个不可变的字符序列,因此当您添加两个字符串时,VM将创建一个新的字符串值,并将两个操作数连接起来

StringBuilder提供了一个可变的字符序列,您可以使用它来连接不同的值或变量,而无需创建新的字符串对象,因此它有时比使用字符串更有效

这提供了一些有用的特性,例如更改在另一个方法中作为参数传递的字符序列的内容,这是字符串所不能做到的

private void addWhereClause(StringBuilder sql, String column, String value) {
   //WARNING: only as an example, never append directly a value to a SQL String, or you'll be exposed to SQL Injection
   sql.append(" where ").append(column).append(" = ").append(value);
}

更多信息请访问

当您已经拥有了所有要附加的“片段”时,使用
StringBuilder根本没有意义。根据示例代码在同一个调用中使用
StringBuilder
和字符串连接更糟糕

这样会更好:

return "select id1, " + " id2 " + " from " + " table";
在本例中,字符串串联实际上是在编译时进行的,因此它相当于更简单的:

return "select id1, id2 from table";
在这种情况下,使用
new StringBuilder().append(“select id1,”).append(“id2”)……toString()
实际上会影响性能,因为它会强制在执行时执行连接,而不是在编译时执行连接。哎呀

如果真正的代码是通过在查询中包含值来构建SQL查询,那么这是另一个单独的问题,即您应该使用参数化查询,在参数中而不是在SQL中指定值


StringBuilder
出现之前,我写了一篇文章。这些原则同样适用于
StringBuilder

您可以猜测,使用StringBuilder的目的没有实现,至少没有达到其全部程度

但是,当编译器从“+”表“
中看到表达式“
”select id1“+”id2“+”时,它会发出一些代码,这些代码实际上在幕后创建了一个
StringBuilder
,并附加到它之后,因此最终的结果并不是那么糟糕


当然,任何人看到这些代码都会认为它有点迟钝。

你也可以使用它。

[[这里有一些很好的答案,但我发现它们仍然缺少一些信息。]]

return (new StringBuilder("select id1, " + " id2 " + " from " + " table"))
     .toString();
正如你所指出的,你给出的例子是一个简单化的例子,但我们还是来分析一下。这里发生的是
return (new StringBuilder("select id1, " + " id2 " + " from " + " table"))
     .toString();
return new StringBuilder("select id1,  id2  from  table").toString();
// the compiler combines these constant strings
return "select id1, " + " id2 " + " from " + " table";
// an internal StringBuilder is used here
return "select id1, " + fieldName + " from " + tableName;
StringBuilder sb = new StringBuilder("select id1, ");
sb.append(fieldName).append(" from ").append(tableName);
return sb.toString();
// 1 StringBuilder used in this line
String query = "select id1, " + fieldName + " from " + tableName;
if (where != null) {
   // another StringBuilder used here
   query += ' ' + where;
}
// choose a good starting size to lower chances of reallocation
StringBuilder sb = new StringBuilder(64);
sb.append("select id1, ").append(fieldName).append(" from ").append(tableName);
// conditional code
if (where != null) {
   sb.append(' ').append(where);
}
return sb.toString();
private void addWhere(StringBuilder sb) {
   if (where != null) {
      sb.append(' ').append(where);
   }
}
sb.append("select " + fieldName);
sb.append("select ").append(fieldName);