The rule
“不要用 + 连接字符串!!!”
是错误的,因为它不完整,因此具有误导性。
规则是
不要用 + 连接字符串循环中
这条规则仍然有效。最初的规则从来不打算应用于循环之外!
一个简单的循环
String s = "";
for (int i = 0; i < 10000; i++) { s += i; }
System.out.println(s);
仍然比
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10000; i++) { sb.append(i); }
System.out.println(sb.toString());
因为 Java 编译器必须将第一个循环翻译为
String s = "";
for (int i = 0; i < 1000; i++) { s = new StringBuilder(s).append(i).toString(); }
System.out.println(s);
还有索赔
如今,JVM 将 + 符号编译为字符串生成器(在大多数情况下)。
至少是有误导性的,因为这个翻译已经用Java 1.0完成了(好吧,不是用StringBuilder而是用StringBuffer,因为StringBuilder只是在Java5中添加的)。
人们还可以争辩说,这一主张
如今,JVM 将 + 符号编译为字符串生成器(在大多数情况下)。
根本就是错误的,因为编译不是由 JVM 完成的。它是由 Java 编译器完成的。
对于问题:Java编译器什么时候使用StringBuilder.append()
它什么时候使用其他机制?
Java编译器(版本1.8)的源代码包含两个地方,通过+
运算符被处理。
- 第一个地方是字符串常量折叠(http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/com/sun/tools/javac/comp/ConstFold.java?av=f#314 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/com/sun/tools/javac/comp/ConstFold.java?av=f#314)。在这种情况下,编译器可以计算结果字符串并使用结果字符串。
- 第二个地方是编译器创建赋值操作代码的地方(http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/com/sun/tools/javac/jvm/Gen.java?av=f#2056 http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/com/sun/tools/javac/jvm/Gen.java?av=f#2056)。在这种情况下,编译器总是发出代码来创建
StringBuilder
结论是,对于 OpenJDK 的 Java 编译器(这意味着 Oracle 分发的编译器),这句话在多数情况下 means always。 (尽管 Java 9 可能会改变这种情况,或者可能是另一个 Java 编译器(例如 Eclipse 中包含的编译器)使用某种其他机制)。