我读了Jon Skeet关于用+连接字符串的答案。我想知道编译器是否还可以识别使用 StringBuffer/StringBuilder 附加的 constand 字符串。
此用于构造 URL 的代码具有良好的意图:
StringBuffer sb = new StringBuffer(constant1);
sb.append(nonconstant);
sb.append("?");
sb.append(constant2);
sb.append("=");
sb.append(constant3);
sb.append("&");
sb.append(constant4);
sb.append("=");
sb.append(constant5);
但是,如果编译器没有针对常量优化 Stringbuffer.append((,我会说以下代码会更有效:
StringBuffer sb = new StringBuffer(constant1);
sb.append(non-constant);
sb.append("?" + constant2 + "=" + constant3 + "&" + constant4 + "=" + constant5);
因为编译器会在编译时优化+
字符串串联。
为什么不试试呢?在java 1.7中,主方法有以下类:
公共类 Concat1{ 私有静态最终字符串常量2 = "c2"; 私有静态最终字符串常量3 = "c3"; public void main(String[] args( { StringBuilder sb = new StringBuilder((; sb.append(args[0](; sb.append("?"(; sb.append(constant2(; sb.append("="(; sb.append(constant3(; System.out.println(sb.toString(((; }}
(为了清楚起见,我更改了常量的数量(产生以下字节码:
公共类 Concat1 { 公共康卡特1((; 法典: 0: aload_0 1: invokespecial #1//Method java/lang/Object.":((V 4:返回 public void main(java.lang.String[](; 法典: 0: new #2//class java/lang/StringBuilder 3:重复 4: invokespecial #3//Method java/lang/StringBuilder.":((V 7:astore_2 8: aload_2 9: aload_1 10: iconst_0 11: aaload 12: invokevirtual #4//Method java/lang/StringBuilder.append:(Ljava/lang/String;(Ljava/lang/StringBuilder; 15:流行音乐 16: aload_2 17: LDC #5//字符串 ? 19: invokevirtual #4//Method java/lang/StringBuilder.append:(Ljava/lang/String;(Ljava/lang/StringBuilder; 22:流行音乐 23: aload_2 24: LDC #6//字符串 c2 26: invokevirtual #4//Method java/lang/StringBuilder.append:(Ljava/lang/String;(Ljava/lang/StringBuilder; 29:流行音乐 30:aload_2 31: LDC #7//字符串 = 33: invokevirtual #4//Method java/lang/StringBuilder.append:(Ljava/lang/String;(Ljava/lang/StringBuilder; 36:流行音乐 37: aload_2 38: LDC #8//字符串 c3 40: invokevirtual #4//Method java/lang/StringBuilder.append:(Ljava/lang/String;(Ljava/lang/StringBuilder; 43:流行音乐 44: getstatic #9//Field java/lang/System.out:Ljava/io/PrintStream; 47:aload_2 48: invokevirtual #10//Method java/lang/StringBuilder.toString:((Ljava/lang/String; 51: invokevirtual #11//Method java/io/PrintStream.println:(Ljava/lang/String;(V 54:返回 }
而以下类:
公共类 Concat2{ 私有静态最终字符串常量2 = "c2"; 私有静态最终字符串常量3 = "c3"; public void main(String[] args( { StringBuilder sb = new StringBuilder((; sb.append(args[0](; sb.append("?" + 常量 2 + "=" + 常量 3(; System.out.println(sb.toString(((; }}
编译为:
公共类 Concat2 { 公共 Concat2((; 法典: 0: aload_0 1: invokespecial #1//Method java/lang/Object.":((V 4:返回 public void main(java.lang.String[](; 法典: 0: new #2//class java/lang/StringBuilder 3:重复 4: invokespecial #3//Method java/lang/StringBuilder.":((V 7:astore_2 8: aload_2 9: aload_1 10: iconst_0 11: aaload 12: invokevirtual #4//Method java/lang/StringBuilder.append:(Ljava/lang/String;(Ljava/lang/StringBuilder; 15:流行音乐 16: aload_2 17: LDC #5//字符串 ?c2=c3 19: invokevirtual #4//Method java/lang/StringBuilder.append:(Ljava/lang/String;(Ljava/lang/StringBuilder; 22:流行音乐 23: getstatic #6//Field java/lang/System.out:Ljava/io/PrintStream; 26: aload_2 27: invokevirtual #7//Method java/lang/StringBuilder.toString:((Ljava/lang/String; 30: invokevirtual #8//Method java/io/PrintStream.println:(Ljava/lang/String;(V 33:返回 }
所以显然,你是对的。在第二类中,StringBuilder
的追加方法只被调用两次,而在第一个情况下,它被调用为每个常量字符串。
编译器是否为常量字符串优化了 Stringbuffer/StringBuilder.append((?
不。
但是,我认为您提出的优化的前提是不正确的。 我建议你把代码的两个版本编译一下。 然后使用 javap
查看编译后的代码在每种情况下的外观。
(FWIW,我希望您的"优化"不会改善生成的代码。 它很可能会创建第二个StringBuilder
来连接中间字符串,并将其转换为字符串。 您最终会得到大致相同数量的append
操作,再加上创建一个额外的临时 StringBuilder 和一个额外的临时字符串。