有了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."<init>":()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."<init>":()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."<init>":()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."<init>":()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."<init>":()V ...... 58: return
我们注意第9行和第29行,分别对应源码的下面两行。
String e = a + b;e = e + c;
这两句,竟然分别创建了一个StringBuilder,如果你再多写几个“+”操作,就会多创建几个StringBuilder,也即是说,每个“+”的出现,都会有一个StringBuilder被new出来,这个开销实在太大了。由此看来“+”还是不能完全替代StringBuilder,只能在极简情况下可以这样理解。
知道了这个结果,那我们就应该明白,假如你有一个for(或while)循环,里面有字符串的拼接操作,你应该使用“+”还是使用StringBuilder呢?
有了Java8的“+”真的可以不要StringBuilder了吗的更多相关文章
- Java之String重点解析
String s = new String("abc")这段代码创建了几个对象呢?s=="abc"这个判断的结果是什么?s.substring(0,2).int ...
- java8实战一------解决冗杂,java8真的很便利(抛砖)
你的代码很容易因为需求而变化,对自己代码改来改去的你一定会觉得烦的.在我看来,java8很容易的解决了这个问题. 先来看看例子!在一堆苹果里,筛选绿色的苹果.当然,Apple类是这样子. class ...
- 【Java8新特性】重复注解与类型注解,你真的学会了吗?
写在前面 在Java8之前,在某个类或者方法,字段或者参数上标注注解时,同一个注解只能标注一次.但是在Java8中,新增了重复注解和类型注解,也就是说,从Java8开始,支持在某个类或者方法,字段或者 ...
- 使用Java8改造出来的模板方法真的是yyds
GitHub 21.3k Star 的Java工程师成神之路,不来了解一下吗! GitHub 21.3k Star 的Java工程师成神之路,真的不来了解一下吗! 我们在日常开发中,经常会遇到类似的场 ...
- Java8 新特性之Stream----java.util.stream
这个包主要提供元素的streams函数操作,比如对collections的map,reduce. 例如: int sum = widgets.stream() .filter(b -> b.ge ...
- Java String 对象,你真的了解了吗?
String 对象的实现 String对象是 Java 中使用最频繁的对象之一,所以 Java 公司也在不断的对String对象的实现进行优化,以便提升String对象的性能,看下面这张图,一起了解一 ...
- JAVA8的java.util.function包 @FunctionalInterface
1 函数式接口java.util.function https://www.cnblogs.com/CobwebSong/p/9593313.html 2 JAVA8的java.util.functi ...
- Java8 新特性(二)- Stream
Stream 用来处理集合数据的,通过 stream 操作可以实现 SQL 的拥有的大部分查询功能 Java8 API 官方文档 下面借助例子,演示 stream 操作 Java userList 列 ...
- 关于String中+与StringBuilder的问题
字符串连接可以通过两种方法实现,其中一种是在Java中提供的一个StringBuilder类(这个类只在J2SE5及以上版本提供,以前的版本使用StringBuffer类). 字符串是Java程序 ...
随机推荐
- liunx中文件夹不能删除怎么操作
1.运行rm -rf 文件名称 2.不能删除对应文件并且提示"rm: cannot remove './.user.ini': Operation not permitted" 操 ...
- 一、Github+Pycharm基础
GitHub为版本管理工具 常用的版本管理工具:本地化版本管理系统.集中式版本管理系统SVN.分布式版本管理系统 一.安装git(自行百度) 二.文件操作与分支管理基础 1.版本控制系统分类 集中化版 ...
- 使用FileStream读写数据
这节讲一下使用FileStream读写数据,这是一个比较基础的流. FileStream类只能处理原始字节,所以它可以处理任何类型的文件. 先看一下它的构造方法: FileStream fs = ne ...
- 服务器硬件必须支持M2 或PCIE才能支持NVME
兆芯服务器不支持NVME. 服务器硬件必须支持M2 或PCIE才能支持NVME.1 因为物理接口只有M2 SATA 和PCIE这三中但是NVME只支持M2 和PCIE这2种2所以 NVME不支持SAT ...
- Linux单用户模式(修改密码、运行级别)方法详解
很多新手当面对"忘记 root 账户密码导致无法登陆系统"这个问题时,直接选择重新系统.其实大可不必,我只需要进入 emergency mode(单用户模式)更新 root 账户的 ...
- Ansible_包含和导入playbook文件
一.管理大型的playbook 1️⃣:如果playbook很长或很复杂,我们可以将其分成较小的文件以便于管理 2️⃣:可采用模块化方式将多个playbook组合为一个主要playbook,或者将文件 ...
- 无法开机 如果您的手机黑屏无法开机,可以按照以下方式操作尝试: 如果是,使用原装充电器或使用弱电流方式充电(例如使用电脑USB接口充电)充电15-30分钟后尝试重新开机;注意:电量过低引起的无法开机,刚插入充电器时可能不亮屏但呼吸灯闪烁状态。
https://www.mi.com/service/support/startup 无法开机 如果您的手机黑屏无法开机,可以按照以下方式操作尝试: 技术支持 如何刷机 无法开机 手机自动关机.重启 ...
- exec函数族实例解析-(转自blankqdb)
fork()函数通过系统调用创建一个与原来进程(父进程)几乎完全相同的进程(子进程是父进程的副本,它将获得父进程数据空间.堆.栈等资源的副本.注意,子进程持有的是上述存储空间的"副本&quo ...
- 6.3-4 zip、unzip
zip:打包和压缩文件 zip压缩格式是Windows与Linux等多平台通用的压缩格式.和gzip命令相比,zip命令压缩文件不仅不会删除源文件,而且还可以压缩目录. zip命令的参数选 ...
- NB-IOT技术与发展问答
http://blog.csdn.net/pan0755/article/details/70145936 该部分分享的是物联网各垂直应用领域里,NB-IoT技术的部署,看看适合NB-IoT技术的垂直 ...