首页
首页 教程 派聪明
  • 首页
  • 教程
  • 派聪明
  • 登录
登录技术派畅享更多权益

用户名密码登录

其他登录:
icon_GitHubCreated with sketchtool.
绑定星球,畅享VIP服务

微信扫码/长按识别登录

输入验证码
有效期五分钟 👉 手动刷新

登录即同意 用户协议 和 隐私政策

绑定二哥编程星球,畅享 VIP 尊享服务!

戳我了解如何获取星球编号,新窗口打开

添加二哥微信 itwanger 审核更快

记得备注 星球编号
我会根据星球编号进行审核
1
教程简介
更新时间: 2023年02月22日
限免
2
Java简介,什么是 Java?
更新时间: 2023年02月22日
限免
3
安装Java开发工具包JDK
更新时间: 2023年02月22日
限免
4
安装集成开发环境Intellij IDEA
更新时间: 2023年02月22日
限免
5
编写第一个Java程序:Hello World
更新时间: 2023年02月22日
限免
6
关键字和保留字
更新时间: 2023年04月23日
限免
7
Java 注释
更新时间: 2023年04月23日
限免
8
Java 数据类型
更新时间: 2023年04月24日
限免
9
Java 数据类型转换
更新时间: 2023年04月24日
限免
10
Java 基本数据类型缓存池
更新时间: 2023年04月24日
限免
11
Java 运算符
更新时间: 2023年04月24日
限免
12
Java 流程控制语句
更新时间: 2023年04月24日
限免
13
掌握 Java 数组
更新时间: 2023年04月25日
限免
14
掌握 Java 二维数组
更新时间: 2023年04月25日
限免
15
打印 Java 数组
更新时间: 2023年04月25日
限免
16
解读 String 源码
更新时间: 2023年04月25日
限免
17
String 为什么不可变
更新时间: 2023年04月25日
限免
18
深入理解 Java 字符串常量池
更新时间: 2023年04月25日
限免
19
详解 String.intern 方法
更新时间: 2023年04月25日
限免
20
StringBuilder 和 StringBuffer
更新时间: 2023年04月25日
限免
21
判断字符串相等
更新时间: 2023年04月25日
限免
22
字符串拼接
更新时间: 2023年04月25日
限免
23
字符串拆分
更新时间: 2023年04月25日
限免
关注公众号
原创
最优雅的Java字符串String拼接是哪种方式?

4.10 String拼接

“哥,你让我看的《Java 开发手册》上有这么一段内容:循环体内,拼接字符串最好使用 StringBuilder 的 append() 方法,而不是 + 号操作符。这是为什么呀?”三妹疑惑地问。

“其实这个问题,我们之前已经聊过。”我慢吞吞地回答道,“不过,三妹,哥今天来给你深入地讲讲。”

PS:三妹能在学习的过程中不断地发现问题,让我感到非常的开心。其实很多时候,我们不应该只是把知识点记在心里,还应该问一问自己,到底是为什么,只有迈出去这一步,才能真正的成长起来。

javap 探究+号操作符拼接字符串的本质

“+ 号操作符其实被 Java 在编译的时候重新解释了,换一种说法就是,+ 号操作符是一种语法糖,让字符串的拼接变得更简便了。”一边给三妹解释,我一边在 Intellij IDEA 中敲出了下面这段代码。

class Demo {
    public static void main(String[] args) {
        String chenmo = "沉默";
        String wanger = "王二";
        System.out.println(chenmo + wanger);
    }
}

在 Java 8 的环境下,使用 javap -c Demo.class 反编译字节码后,可以看到以下内容:

Compiled from "Demo.java"
class Demo {
  Demo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String 沉默
       2: astore_1
       3: ldc           #3                  // String 王二
       5: astore_2
       6: getstatic     #4                  // Field java/lang/System.out:Ljava/io/PrintStream;
       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: invokevirtual #9                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      30: return
}

“你看,三妹,这里有一个 new 关键字,并且 class 类型为 java/lang/StringBuilder。”我指着标号为 9 的那行对三妹说,“这意味着新建了一个 StringBuilder 的对象。”

“然后看标号为 17 的这行,是一个 invokevirtual 指令,用于调用对象的方法,也就是 StringBuilder 对象的 append() 方法。”

“也就意味着把 chenmo("沉默")这个字符串添加到 StringBuilder 对象中了。”

“再往下看,标号为 21 的这行,又调用了一次 append() 方法,意味着把 wanger("王二")这个字符串添加到 StringBuilder 对象中了。”

换成 Java 代码来表示的话,大概是这个样子:

class Demo {
    public static void main(String[] args) {
        String chenmo = "沉默";
        String wanger = "王二";
        System.out.println((new StringBuilder(chenmo)).append(wanger).toString());
    }
}

“哦,原来编译的时候把“+”号操作符替换成了 StringBuilder 的 append() 方法啊。”三妹恍然大悟。

“是的,不过到了 Java 9(不是长期支持版本,所以我会拿 Java 11 来演示),情况发生了一些改变,同样的代码,字节码指令完全不同了。”我说。

同样的代码,在 Java 11 的环境下,字节码指令是这样的:

Compiled from "Demo.java"
public class com.itwanger.thirtyseven.Demo {
  public com.itwanger.thirtyseven.Demo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: ldc           #2                  // String
       2: astore_1
       3: iconst_0
       4: istore_2
       5: iload_2
       6: bipush        10
       8: if_icmpge     41
      11: new           #3                  // class java/lang/String
      14: dup
      15: ldc           #4                  // String 沉默
      17: invokespecial #5                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
      20: astore_3
      21: ldc           #6                  // String 王二
      23: astore        4
      25: aload_1
      26: aload_3
      27: aload         4
      29: invokedynamic #7,  0              // InvokeDynamic #0:makeConcatWithConstants:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
      34: astore_1
      35: iinc          2, 1
      38: goto          5
      41: return
}

看标号为 29 的这行,字节码指令为 invokedynamic,该指令允许由应用级的代码来决定方法解析,所谓的应用级的代码其实是一个方法——被称为引导方法(Bootstrap Method),简称 BSM,BSM 会返回一个 CallSite(调用点) 对象,这个对象就和 invokedynamic 指令链接在一起。以后再执行这条 invokedynamic 指令时就不会创建新的 CallSite 对象。CallSite 其实就是一个 MethodHandle(方法句柄)的 holder,指向一个调用点真正执行的方法——此时就是 StringConcatFactory.makeConcatWithConstants() 方法。

“哥,你别再说了,再说我就听不懂了。”三妹打断了我的话。

“好吧,总之就是 Java 9 以后,JDK 用了另外一种方法来动态解释 +

登录之后即可阅读全文
该文档仅「二哥编程星球」的VIP用户可见

二哥的编程星球内容包括:

1. 付费文档: 技术派、MYDB 等项目配套的 120+篇教程查看权限

2. 面试指南: 校招、社招的 40 万+字面试求职攻略

3. 智能助手: 无限期使用派聪明 AI 助手,已对接讯飞星火和 ChatGPT双通道,不用花 1 分钱

4. 专属问答: 向二哥 1v1 发起提问,内容不限于 offer 选择、学习路线、职业规划等

5. 简历修改: 提供简历修改服务,附赠星球 100+优质简历模板可供参考

6. 学习环境: 打造一个沉浸式的学习环境,有一种高考冲刺、大学考研的氛围


二哥的星球

》步骤①:微信扫描上方二维码,点击「加入知识星球」按钮

》步骤②:访问星球置顶帖球友必看: https://t.zsxq.com/11rEo9Pdu,获取项目配套文档的语雀访问地址和密码

已加入星球,绑定星球编号
删除提醒

确定删除《最优雅的Java字符串String拼接是哪种方式?》吗

真诚点赞 诚不我欺

回复