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."
希望这能稍微揭开内部的神秘面纱!
我正在处理一个包含大量遗留代码的应用程序。
我经常看到的是在 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."
希望这能稍微揭开内部的神秘面纱!