在Java中字符串是一个常用的东西,而其一个常用的操作是字符串的拼接,Java对此提供了一种非常直观的操作方式——即 + 操作符。
1String str0 = "a"; 2String str1 = str0 + "b";
如上的程序片段就实现了一个字符串的拼接,可以看到整个描述非常简洁,一目了然。学习Java的时候,这是接触到的第一种拼接字符串的方式,先入为主的印象会让你在以后需要拼接字符串时第一时间想到它,但是,你会听到大家都说:拼接大量的字符串时使用StringBuilder更好,这是为什么呢?
让我们看看编译器是怎么处理 + 操作符的。下面是上一个程序片段编译后的字节码指令:
1 public static void main(java.lang.String[]); 2 Code: 3 0: ldc #2 // String a 4 2: astore_1 5 3: new #3 // class java/lang/StringBuilder 6 6: dup 7 7: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 8 10: aload_1 9 11: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 10 14: ldc #6 // String b 11 16: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 12 19: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 13 22: astore_2 14 23: return 15}
通过阅读这段字节码,可以发现,我们的**+**拼接操作实际上被编译器理解成了这个样子:
1String str0 = "a"; 2StringBuilder sb = new StringBuilder(); 3sb.append(str0).append("b"); 4String str1 = sb.toString();
中间多出来了一个StringBuilder对象,这是一个临时对象。
假设我们现在有这样一段程序片段:
1String str0 = "a"; 2for (int i = 0; i < 10000; i++) { 3 str0 += "a"; 4}
再来看看编译后的字节码:
1 public static void main(java.lang.String[]); 2 Code: 3 0: ldc #2 // String a 4 2: astore_1 5 3: iconst_0 6 4: istore_2 7 5: iload_2 8 6: sipush 10000 9 9: if_icmpge 38 10 12: new #3 // class java/lang/StringBuilder 11 15: dup 12 16: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V 13 19: aload_1 14 20: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 15 23: ldc #2 // String a 16 25: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 17 28: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 18 31: astore_1 19 32: iinc 2, 1 20 35: goto 5 21 38: return 22}
5~35是我们的循环体,在循环体里面,每次拼接都会生成一个StringBuilder的临时对象,那么这个程序片段执行下去就会产生10000个StringBuilder的临时对象,这10000个临时对象都是必要的吗?显然不是,我们可以在循环体外直接创建一个StringBuilder对象,然后在循环体中通过append方法拼接字符串,这样就省下了创建并回收10000个临时对象的消耗。
所以,在需要拼接大量字符串时,还是使用StringBuilder对象为好。