Java 编译器如何处理 StringBuilder(a + b + c + d)?

How does the Java compiler handle StringBuilder(a + b + c + d)?

我正在处理一个包含大量遗留代码的应用程序。 我经常看到的是在 StringBuilder 参数中使用“+”进行字符串连接。
示例:

StringBuilder sb = new StringBuilder("This "
        + "looks "
        + "rather "
        + "weird "
        + "to "
        + "me.") 

据我所知,编译器将使用 + 运算符的字符串连接替换为 StringBuilder().append()。
恐怕现在编译器会创建一个临时的 StringBuilder 来执行连接,然后转换 toString() 并将结果插入现有的 StringBuilder。

我的问题是:编译器是否能够优化掉嵌套的 StringBuilder?如果不是,我应该重写代码以节省几个 CPU 周期吗?明明是好用,但是每次看都觉得眼睛疼。

感谢您的任何见解!

编译器优化了字符串常量的串联:

StringBuilder sb = new StringBuilder("This "
        + "looks "
        + "rather "
        + "weird "
        + "to "
        + "me.");

在运行时完全等同于:

StringBuilder sb = new StringBuilder("This looks rather weird to me.");

这也适用于任何字符串编译时常量,而不仅仅是文字。

您可能已经读到有关使用非常量的问题:

StringBuilder sb = new StringBuilder("This "
        + arg1
        + var2);

相当于:

StringBuilder sb = new StringBuilder(new StringBuilder("This ")
        .append(arg1)
        .append(var2)
        .toString());

如果您将其重写为:

,它会给您带来更好的性能
StringBuilder sb = new StringBuilder("This ")
        .append(arg1)
        .append(var2);

要添加到@k314159 的答案中,确定编译器如何转换任何特定代码块很容易,如果您将它隔离,使用 javac 编译它,然后 运行 使用 运行 反汇编程序 javap 查看生成的(带注释的)字节码。

例如,如果您将 StringBuilder 代码隔离成最小的 class:

public class Test {
    public static void main(String[] args) {
        StringBuilder sb = new StringBuilder("This "
        + "looks "
        + "rather "
        + "weird "
        + "to "
        + "me.");
    }
}

然后您可以像这样编译和反汇编输出:

javac Test.java && javap -c Test.class

其输出为:

Compiled from "Test.java"
public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: new           #2                  // class java/lang/StringBuilder
       3: dup
       4: ldc           #3                  // String This looks rather weird to me.
       6: invokespecial #4                  // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
       9: astore_1
      10: return
}

在这种情况下,您感兴趣的行会在这里:

4: ldc           #3                  // String This looks rather weird to me.

您可能已经猜到了,这条指令将一个(字符串)常量压入堆栈。注释中明确指出,本例中的常量是单个字符串,即:"This looks rather weird to me."

希望这能稍微揭开内部的神秘面纱!