编译器是否针对常量字符串优化了 Stringbuffer/StringBuilder.append()



我读了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 和一个额外的临时字符串。

最新更新