为了账号安全,请及时绑定邮箱和手机立即绑定

字节码理解Java String的两种创建方式

标签:
Java

字节码理解Java String的两种创建方式

前言

通过该篇文章你将获得:

  1. 通过字节码理解Java String的两种创建方式
  2. 理解两种创建方式的不同。
  3. 加深理解Java的内存存储方式。

基础知识讲解

名词解释

  1. 字面量:诸如"abc"、"123"等,由两个引号引起来的部分叫做字面量,其在编译阶段就可确定。
  2. 常量池:存放常量的地方。

实例代码讲解

我们将通过以下代码和其字节码理解String的两种创建方式。

package com.pai;

public class StringInit {
    public static void main(String[] args) {
        String str1 = "abc";
        String str2 = new String("def");
    }
}

经过编译(javac StringInit.java)与反编译(javap -v StringInit.class),我们可以查看到如下的常量池:

Constant pool:
   #1 = Methodref          #7.#16         // java/lang/Object."<init>":()V
   #2 = String             #17            // abc
   #3 = Class              #18            // java/lang/String
   #4 = String             #19            // def
   #5 = Methodref          #3.#20         // java/lang/String."<init>":(Ljava/lang/String;)V
   #6 = Class              #21            // com/pai/StringInit
   #7 = Class              #22            // java/lang/Object
   #8 = Utf8               <init>
   #9 = Utf8               ()V
  #10 = Utf8               Code
  #11 = Utf8               LineNumberTable
  #12 = Utf8               main
  #13 = Utf8               ([Ljava/lang/String;)V
  #14 = Utf8               SourceFile
  #15 = Utf8               StringInit.java
  #16 = NameAndType        #8:#9          // "<init>":()V
  #17 = Utf8               abc
  #18 = Utf8               java/lang/String
  #19 = Utf8               def
  #20 = NameAndType        #8:#23         // "<init>":(Ljava/lang/String;)V
  #21 = Utf8               com/pai/StringInit
  #22 = Utf8               java/lang/Object
  #23 = Utf8               (Ljava/lang/String;)V

通过常量池我们可以总结如下几点:

  1. 两种创建方法的字面量都会被放到常量池中(#2#4)
  2. 局部变量名不会被放到常量池中(str1str2)
    经过编译(javac StringInit.java)与反编译(javap -v StringInit.class),我们可以查看到如下的指令:
 public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: ldc           #2                  // String abc
         2: astore_1
         3: new           #3                  // class java/lang/String
         6: dup
         7: ldc           #4                  // String def
         9: invokespecial #5                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
        12: astore_2
        13: return
      LineNumberTable:
        line 5: 0
        line 6: 3
        line 7: 13

接下来我们来详细讲解两种创建方式:

使用字面量创建

         0: ldc           #2                  // String abc
         2: astore_1

以下为指令讲解:

  1. 将常量池中#2字面量推送至栈顶。(压入栈顶)
  2. 把栈顶的值存入1号本地变量str1,因此str1引用的是常量池地址。

使用对象创建

         3: new           #3                  // class java/lang/String
         6: dup
         7: ldc           #4                  // String def
         9: invokespecial #5                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
        12: astore_2

以下为指令讲解:

  1. new指令在java堆上为String对象分配内存空间,并将地址压入操作数栈。
  2. dup指令复制操作数栈顶元素(复制String对象的地址,并压入栈中,此时栈顶有两个相同的地址)
  3. ldc指令把常量池中#4字面量推送至栈顶
  4. invokespecial指令调用实例初始化方法String <init>(String),我们也看到该方法有一个参数,是String类型的,我们使用栈顶的字面量当入参数传入初始化方法。同时我们要知道是哪个个对象需要初始化,因此我们又从栈顶弹出一个引用地址(这也是为什么要dup复制一份的原因)
  5. 将栈顶元素(对象地址)赋值给2号本地变量str2

总结

我们可以看出字面量创建只有1个对象,就是常量池中的字面量,本地变量直接引用该地址,入栈1次。

使用对象创建使用了2个对象,常量池和堆,入栈3次。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消