使用字节码理解Java String的两种创建方式|从根上理解JavaString的两种创建方式

本文通过字节码分析了Java中使用字面量和对象创建String的两种方式,探讨了它们在常量池和内存中的不同表现,帮助读者深入理解Java的内存存储和String对象的创建过程。
摘要由CSDN通过智能技术生成

使用字节码理解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次。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

tinpo_123

感谢给小张填杯java~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值