java8 stringbuilder_有了Java8的“+”真的可以不要StringBuilder了吗

最近在头条上看到一篇帖子,说Java8开始,字符串拼接时,“+”会被编译成StringBuilder,所以,字符串的连接操作不用再考虑效率问题了,事实真的是这样吗?要搞明白,还是要看看Java编译后的字节码。

先比较这样两段代码。最简单的字符串拼接,一个用“+”,一个用StringBuilder。

public void useOperator(){ String a = "abc"; String b = "efg"; String c = a + b; System.out.println(c); } public void useStringBuilder(){ String a = "abc"; String b = "efg"; StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(a); stringBuilder.append(b); System.out.println(stringBuilder.toString()); }

用javap去看这个代码的字节码,如下:

public void useOperator(); Code: '''a和b分别被存储到局部变量1和2中''' 0: ldc #2 // String abc 2: astore_1 3: ldc #3 // String efg 5: astore_2 '''"+"被转为StringBuilder''' 6: new #4 // class java/lang/StringBuilder '''复制一个引用,入栈''' 9: dup '''初始化StringBuilder,出栈''' 10: invokespecial #5 // Method java/lang/StringBuilder."":()V '''取出变量a''' 13: aload_1 '''调用append''' 14: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; '''取出变量b''' 17: aload_2 '''调用append''' 18: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 21: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; '''将toString返回的结果保存到局部变量3中,就是变量c''' 24: astore_3 25: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream; '''取出变量c''' 28: aload_3 '''打印结果''' 29: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 32: return

对比源代码,useOperator中的c=a+b,被编译成了使用StringBuilder来操作,并依次把a和b添加到其中,看来确实jvm优化了“+”的拼接功能。

再看看useStringBuilder的字节码:

public void useStringBuilder(); Code: 0: ldc #2 // String abc 2: astore_1 3: ldc #3 // String efg 5: astore_2 6: new #4 // class java/lang/StringBuilder 9: dup 10: invokespecial #5 // Method java/lang/StringBuilder."":()V 13: astore_3 14: aload_3 15: aload_1 16: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; '''append方法是带返回值的,使用invokevirtual指令,如果后面不继续使用返回结果,就需要将其pop出栈,否则后面的使用就乱了''' 19: pop 20: aload_3 21: aload_2 22: invokevirtual #6 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 25: pop 26: getstatic #8 // Field java/lang/System.out:Ljava/io/PrintStream; 29: aload_3 30: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 33: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 36: return

基本上和使用“+”的字节码是一致的,只不过是多了几次aload及pop,核心是一样的。

从上面的比较看,对于单一的字符串拼接,“+”确实等效于StringBuilder。能不能确认“+”是否可以替代StringBuilder,这些还不够,再看看稍微复杂一些的。

三个变量拼接。

public void useOperator(){ String a = "abc"; String b = "efg"; String c = "123"; String e = a + b + c; System.out.println(e); }

public void useOperator(); Code: 0: ldc #2 // String abc 2: astore_1 3: ldc #3 // String efg 5: astore_2 6: ldc #4 // String 123 8: astore_3 9: new #5 // class java/lang/StringBuilder 12: dup 13: invokespecial #6 // Method java/lang/StringBuilder."":()V 16: aload_1 17: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 20: aload_2 21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: aload_3 25: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 28: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 31: astore 4 33: getstatic #9 // Field java/lang/System.out:Ljava/io/PrintStream; 36: aload 4 38: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 41: return

貌似也没什么问题,依旧是对同一个StringBuilder对象操作。

再改一点,两次使用“+”操作符。看看会有什么不同吗?

public void useOperator(){ String a = "abc"; String b = "efg"; String c = "123"; String e = a + b; e = e + c; System.out.println(e); }

public void useOperator(); Code: 0: ldc #2 // String abc 2: astore_1 3: ldc #3 // String efg 5: astore_2 6: ldc #4 // String 123 8: astore_3 '''第一个StringBuilder''' 9: new #5 // class java/lang/StringBuilder 12: dup 13: invokespecial #6 // Method java/lang/StringBuilder."":()V 16: aload_1 17: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 20: aload_2 21: invokevirtual #7 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder; 24: invokevirtual #8 // Method java/lang/StringBuilder.toString:()Ljava/lang/String; 27: astore 4 '''第二个StringBuilder''' 29: new #5 // class java/lang/StringBuilder 32: dup 33: invokespecial #6 // Method java/lang/StringBuilder."":()V ...... 58: return

我们注意第9行和第29行,分别对应源码的下面两行。

String e = a + b;e = e + c;

这两句,竟然分别创建了一个StringBuilder,如果你再多写几个“+”操作,就会多创建几个StringBuilder,也即是说,每个“+”的出现,都会有一个StringBuilder被new出来,这个开销实在太大了。由此看来“+”还是不能完全替代StringBuilder,只能在极简情况下可以这样理解。

知道了这个结果,那我们就应该明白,假如你有一个for(或while)循环,里面有字符串的拼接操作,你应该使用“+”还是使用StringBuilder呢?

在 JDK8 Java引入了一种新的字符串拼接方法,即使用 `StringBuilder` 类来进行字符串拼接。这种方法比传统的字符串拼接方式(使用 `+` 运算符)更加高效。 传统的字符串拼接方式,每次拼接字符串时都会创建一个新的字符串对象,造成频繁的内存分配和回收,影响程序的性能。而使用 `StringBuilder` 类的方式,可以通过在一个可变的字符串缓冲区进行字符串拼接,避免了频繁地创建新的字符串对象,提高了程序的性能。 例如,我们可以使用 `StringBuilder` 类来拼接两个字符串: ```java String str1 = "hello"; String str2 = "world"; StringBuilder sb = new StringBuilder(); sb.append(str1).append(" ").append(str2); String result = sb.toString(); ``` 在上面的代码,我们首先创建了两个字符串 `str1` 和 `str2`。然后,我们创建了一个 `StringBuilder` 对象 `sb`,并使用 `append()` 方法将两个字符串拼接在一起,最后通过 `toString()` 方法将 `StringBuilder` 对象转换为字符串需要注意的是,在 JDK8 字符串拼接也可以使用新的语法糖,即使用 `+` 运算符来拼接字符串,例如: ```java String str1 = "hello"; String str2 = "world"; String result = str1 + " " + str2; ``` 虽然这种方式看起来更简洁,但实际上它仍然会创建多个临时的字符串对象,因此在需要频繁进行字符串拼接的情况下,建议使用 `StringBuilder` 类的方式,以提高程序的性能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值