最近在头条上看到一篇帖子,说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了吗的更多相关文章

  1. Java之String重点解析

    String s = new String("abc")这段代码创建了几个对象呢?s=="abc"这个判断的结果是什么?s.substring(0,2).int ...

  2. java8实战一------解决冗杂,java8真的很便利(抛砖)

    你的代码很容易因为需求而变化,对自己代码改来改去的你一定会觉得烦的.在我看来,java8很容易的解决了这个问题. 先来看看例子!在一堆苹果里,筛选绿色的苹果.当然,Apple类是这样子. class ...

  3. 【Java8新特性】重复注解与类型注解,你真的学会了吗?

    写在前面 在Java8之前,在某个类或者方法,字段或者参数上标注注解时,同一个注解只能标注一次.但是在Java8中,新增了重复注解和类型注解,也就是说,从Java8开始,支持在某个类或者方法,字段或者 ...

  4. 使用Java8改造出来的模板方法真的是yyds

    GitHub 21.3k Star 的Java工程师成神之路,不来了解一下吗! GitHub 21.3k Star 的Java工程师成神之路,真的不来了解一下吗! 我们在日常开发中,经常会遇到类似的场 ...

  5. Java8 新特性之Stream----java.util.stream

    这个包主要提供元素的streams函数操作,比如对collections的map,reduce. 例如: int sum = widgets.stream() .filter(b -> b.ge ...

  6. Java String 对象,你真的了解了吗?

    String 对象的实现 String对象是 Java 中使用最频繁的对象之一,所以 Java 公司也在不断的对String对象的实现进行优化,以便提升String对象的性能,看下面这张图,一起了解一 ...

  7. JAVA8的java.util.function包 @FunctionalInterface

    1 函数式接口java.util.function https://www.cnblogs.com/CobwebSong/p/9593313.html 2 JAVA8的java.util.functi ...

  8. Java8 新特性(二)- Stream

    Stream 用来处理集合数据的,通过 stream 操作可以实现 SQL 的拥有的大部分查询功能 Java8 API 官方文档 下面借助例子,演示 stream 操作 Java userList 列 ...

  9. 关于String中+与StringBuilder的问题

      字符串连接可以通过两种方法实现,其中一种是在Java中提供的一个StringBuilder类(这个类只在J2SE5及以上版本提供,以前的版本使用StringBuffer类). 字符串是Java程序 ...

随机推荐

  1. 基于蒙特卡洛树搜索(MCTS)的多维可加性指标的异常根因定位

    摘要:本文是我在从事AIOps研发工作中做的基于MCTS的多维可加性指标的异常根因定位方案,方案基于清华大学AIOPs实验室提出的Hotspot算法,在此基础上做了适当的修改. 1        概述 ...

  2. SpringBoot邮件报警

    SpringBoot邮件报警 一.介绍 邮件报警,大体思路就是收集服务器发生的异常发送到邮箱,做到服务器出问题第一时间知道,当然要是不关注邮箱当我没说 二.配置邮箱 (1).注册两个邮箱账号(一个用来 ...

  3. C++逆向分析----虚函数与多层继承

    虚函数 C++通过关键字virtual来将函数声明为一个虚函数.当一个类包含虚函数后编译器就会给类的实例对象增加一个虚表指针,所谓的虚表指针就是指向虚表的指针.虚表就是一张地址表,它包含了这个类中所有 ...

  4. 记一次 .NET 某HIS系统后端服务 内存泄漏分析

    一:背景 1. 讲故事 前天那位 his 老哥又来找我了,上次因为CPU爆高的问题我给解决了,看样子对我挺信任的,这次另一个程序又遇到内存泄漏,希望我帮忙诊断下. 其实这位老哥技术还是很不错的,他既然 ...

  5. [bug] Importing maven project 卡在%9不动

    参考 Importing maven project 卡在%9不动 https://blog.csdn.net/weixin_43197380/article/details/89220337

  6. docker swarm外部验证负载均衡时不生效

    问题描述 我在本地创建了3个装了centos7的虚拟机, 并初始化了swarm集群, 即1个manager节点, 2个worker节点; 三台机子的ip分别是 192.168.124.8 - (man ...

  7. 从栈上理解 Go语言函数调用

    转载请声明出处哦~,本篇文章发布于luozhiyun的博客:https://www.luozhiyun.com/archives/518 本文使用的go的源码 1.15.7 前言 函数调用类型 这篇文 ...

  8. Web应用漏洞-NGINX各类请求头缺失对应配置

    前言 随着越来越多的网络访问通过WEB界面进行操作,WEB安全已经成为互联网安全的一个热点,基于WEB的攻击广为流行,SQL注入.跨站脚本等WEB应用层漏洞的存在使得网站沦陷.页面篡改.网页挂马等攻击 ...

  9. [leetcode] 38. 报数(Java)(字符串处理)

    38. 报数 水题 class Solution { public String next(String num) { String ans = ""; int i = 0; wh ...

  10. Lua时间互转

    1. 时间戳转成格式化字符串 直接利用函数os.date()将时间戳转化成格式化字符串. local timestamp = 1561636137; local strDate = os.date(& ...