Java编译器没有优化字符串连接

时间:2014-09-10 14:43:22

标签: java string compiler-construction stringbuilder

我注意到Java编译器没有将String addition(+)转换为StringBuilder.append()方法。我创建了一个只有一个方法的类

public void doSomething(String a, String b) {
    String c = a + "a";
    String d = b + "b";
    String e = c + d;
    String f = e;
    System.out.println(f);
}

编译和反编译后,我的方法看起来像这样:

public void doSomething(String paramString1, String paramString2)
{
    String str1 = paramString1 + "a";
    String str2 = paramString2 + "b";
    String str3 = str1 + str2;
    String str4 = str3;
    System.out.println(str4);
}

为什么编译器没有优化我的代码?我使用ant进行打包和调试设置是错误的。我也尝试过单个java文件的javac,但结果是一样的。

3 个答案:

答案 0 :(得分:3)

您的反编译器确实简化了代码。

考虑这个源文件:

public class foo {
    public void a(String[] args) {
        String b = (new StringBuilder()).append("x").append(args[0]).toString();
    }

    public void b(String[] args) {
        String b = "x" + args[0];
    }
}

javap输出:

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

  public void a(java.lang.String[]);
    Code:
       0: new           #2                  // class java/lang/StringBuilder
       3: dup           
       4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
       7: ldc           #4                  // String x
       9: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      12: aload_1       
      13: iconst_0      
      14: aaload        
      15: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      18: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      21: astore_2      
      22: return        

  public void b(java.lang.String[]);
    Code:
       0: new           #2                  // class java/lang/StringBuilder
       3: dup           
       4: invokespecial #3                  // Method java/lang/StringBuilder."<init>":()V
       7: ldc           #4                  // String x
       9: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      12: aload_1       
      13: iconst_0      
      14: aaload        
      15: invokevirtual #5                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      18: invokevirtual #6                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      21: astore_2      
      22: return        
}

看到字节码基本相同:编译器使用b优化将方法a转换为方法StringBuilder.append

现在让我们看看JD说的话:

public class foo
{
  public void a(String[] paramArrayOfString)
  {
    String str = "x" + paramArrayOfString[0];
  }

  public void b(String[] paramArrayOfString) {
    String str = "x" + paramArrayOfString[0];
  }
}

这是正确的:JD实际上接受函数a并将其解释为字符串添加,即使原始源代码是使用显式StringBuilder指定的! / p>

因此,我们可以看到JD会在检测到正在使用的模式时尝试撤消StringBuilder优化。

答案 1 :(得分:2)

我做了javap -c Test.class并且StringBuilder出现了(Java 8)。

public void doSomething(java.lang.String, java.lang.String);
Code:
   0: new           #2  // class StringBuilder
   3: dup           
   4: invokespecial #3  // Method StringBuilder."<init>":()V
   7: aload_1       
   8: invokevirtual #4  // Method StringBuilder.append:(LString;)LStringBuilder;
  11: ldc           #5  // String a
  13: invokevirtual #4  // Method StringBuilder.append:(LString;)LStringBuilder;
  16: invokevirtual #6  // Method StringBuilder.toString:()LString;
  19: astore_3      
  20: new           #2  // class StringBuilder
  23: dup           
  24: invokespecial #3  // Method StringBuilder."<init>":()V
  27: aload_2       
  28: invokevirtual #4  // Method StringBuilder.append:(LString;)LStringBuilder;
  31: ldc           #7  // String b
  33: invokevirtual #4  // Method StringBuilder.append:(LString;)LStringBuilder;
  36: invokevirtual #6  // Method StringBuilder.toString:()LString;
  39: astore        4
  41: new           #2  // class StringBuilder
  44: dup           
  45: invokespecial #3  // Method StringBuilder."<init>":()V
  48: aload_3       
  49: invokevirtual #4  // Method StringBuilder.append:(LString;)LStringBuilder;
  52: aload         4
  54: invokevirtual #4  // Method StringBuilder.append:(LString;)LStringBuilder;
  57: invokevirtual #6  // Method StringBuilder.toString:()LString;
  60: astore        5
  62: aload         5
  64: astore        6
  66: getstatic     #8  // Field System.out:Ljava/io/PrintStream;
  69: aload         6
  71: invokevirtual #9  // Method java/io/PrintStream.println:(LString;)V
  74: return        

我认为反编译器试图简化它以实现自然编码。

答案 2 :(得分:0)

Java编译器在编译时不进行任何优化,因为它在运行时留给了JIT。如果要在多个语句中有效地构建字符串,则应使用StringBuilder

+运算符编译为StringBuilder对象的调用。当你写:

String c = a + "a";

编译器生成的代码就像你写的那样:

String c = new StringBuilder().append(a).append("a").toString();

如果在单个表达式中多次使用+,例如a + "a" + b + "b",编译器将对整个表达式使用单个StringBuilder,并调用append个必要时。但它并没有将多个语句优化为单个等效表达式,因此如果要使用单个StringBuilder将所有字​​符串连接在一起,则需要将其作为单个表达式编写或使用{{ 1}}明确地在您的代码中。