我注意到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,但结果是一样的。
答案 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}}明确地在您的代码中。