JAVA String a = “abc“ 中发生了什么

JAVA String a = "abc" 中发生了什么

和上次int a = 1 后续

测试代码

javac TestCode.java

 public class TestCode {
     public static void main(String[] args) {
         // 此处写aaaa是便于看字节码
         String aaaa = "bbbb";
         aaaa = "cccc";
     }
 }

反编译

javap -v -p -l TestCode(l是小写的L)

注解:javap是jdk自带的反解析工具。作用是根据class字节码文件,反解析出当前类对应的code区(汇编指令)、本地变量表、异常表和代码行偏移量映射表、常量池等等信息。

 Classfile xxxxx/com/code/baseCode/TestCode.class
   Last modified 2021-11-22; size 475 bytes
   MD5 checksum 0a108c035194f620555926cf7d113c8b
   Compiled from "TestCode.java"
 public class com.code.baseCode.TestCode
   minor version: 0
   major version: 52
   flags: ACC_PUBLIC, ACC_SUPER
 Constant pool:
    #1 = Methodref          #5.#21         // java/lang/Object."<init>":()V
    #2 = String             #22            // bbbb
    #3 = String             #23            // cccc
    #4 = Class              #24            // com/code/baseCode/TestCode
    #5 = Class              #25            // java/lang/Object
    #6 = Utf8               <init>
    #7 = Utf8               ()V
    #8 = Utf8               Code
    #9 = Utf8               LineNumberTable
   #10 = Utf8               LocalVariableTable
   #11 = Utf8               this
   #12 = Utf8               Lcom/code/baseCode/TestCode;
   #13 = Utf8               main
   #14 = Utf8               ([Ljava/lang/String;)V
   #15 = Utf8               args
   #16 = Utf8               [Ljava/lang/String;
   #17 = Utf8               aaaa
   #18 = Utf8               Ljava/lang/String;
   #19 = Utf8               SourceFile
   #20 = Utf8               TestCode.java
   #21 = NameAndType        #6:#7          // "<init>":()V
   #22 = Utf8               bbbb
   #23 = Utf8               cccc
   #24 = Utf8               com/code/baseCode/TestCode
   #25 = Utf8               java/lang/Object
 {
   public com.code.baseCode.TestCode();
     descriptor: ()V
     flags: ACC_PUBLIC
     Code:
       stack=1, locals=1, args_size=1
          0: aload_0
          1: invokespecial #1                  // Method java/lang/Object."<init>":()V
          4: return
       LineNumberTable:
         line 8: 0
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0       5     0  this   Lcom/code/baseCode/TestCode;
 ​
   public static void main(java.lang.String[]);
     descriptor: ([Ljava/lang/String;)V
     flags: ACC_PUBLIC, ACC_STATIC
     Code:
       stack=1, locals=2, args_size=1
          0: ldc           #2                  // String bbbb
          2: astore_1
          3: ldc           #3                  // String cccc
          5: astore_1
          6: return
       LineNumberTable:
         line 11: 0
         line 12: 3
         line 55: 6
       LocalVariableTable:
         Start  Length  Slot  Name   Signature
             0       7     0  args   [Ljava/lang/String;
             3       4     1  aaaa   Ljava/lang/String;
 }
 SourceFile: "TestCode.java"

反编译后,会发现aaaa存在于Constant pool(常量池)和LocalVariableTable(本地变量表)中

同时发现和int a = 1的区别在于字符串“bbbb”和“cccc”也会在常量池中

JVM中常量池的划分,请看上篇文章

图片解释

java1.8的内存布局

image-20211122224530763

在编译后内存情况

image-20211123212545822

运行时发生情况

如何查看Java bytecode指令?

https://en.wikipedia.org/wiki/List_of_Java_bytecode_instructions

Jvm系列3—字节码指令 - Gityuan博客 | 袁辉辉的技术博客

其中java byte code为

 0: ldc           #2                  // String bbbb
 2: astore_1
 3: ldc           #3                  // String cccc
 5: astore_1

ldc:push a constant #index from a constant pool (String, int, float, Class, java.lang.invoke.MethodType, java.lang.invoke.MethodHandle, or a dynamically-computed constant) onto the stack

意思就是:int、float或String型等常量从常量池推送至栈顶

astore_1:store a reference into local variable 1

意思就是将:一个引用放入到局部变量1中(也就是aaaa中)

ldc #2

image-20211123213852082

astore_1

image-20211123214132207

ldc #3

image-20211123214251552

astore_1

image-20211123214346740

图片解释:用new String()创建

将代码改成如下:

 public class TestCode {
     public static void main(String[] args) {
         String aaaa = new String("bbbb");
         aaaa = new String("cccc");
     }
 }

则会发现常量池多了一些东西,并且执行指令多了。

其中执行指令如下

 0: new           #2                  // class java/lang/String
 3: dup
 4: ldc           #3                  // String bbbb
 6: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
 9: astore_1
 10: new           #2                  // class java/lang/String
 13: dup
 14: ldc           #5                  // String cccc
 16: invokespecial #4                  // Method java/lang/String."<init>":(Ljava/lang/String;)V
 19: astore_1

new:创建类实例

dup:复制数值,并压入栈顶

ldc:从常量池推送至栈顶

invokespecial:调用特殊实例方法(包括实例初始化方法、父类方法)

astore_1:一个引用放入到局部变量1中(也就是aaaa中)

常量池如下

 Constant pool:
    #1 = Methodref          #7.#23         // java/lang/Object."<init>":()V
    #2 = Class              #24            // java/lang/String
    #3 = String             #25            // bbbb
    #4 = Methodref          #2.#26         // java/lang/String."<init>":(Ljava/lang/String;)V
    #5 = String             #27            // cccc
    #6 = Class              #28            // com/code/baseCode/TestCode
    #7 = Class              #29            // java/lang/Object
    #8 = Utf8               <init>
    #9 = Utf8               ()V
   #10 = Utf8               Code
   #11 = Utf8               LineNumberTable
   #12 = Utf8               LocalVariableTable
   #13 = Utf8               this
   #14 = Utf8               Lcom/code/baseCode/TestCode;
   #15 = Utf8               main
   #16 = Utf8               ([Ljava/lang/String;)V
   #17 = Utf8               args
   #18 = Utf8               [Ljava/lang/String;
   #19 = Utf8               aaaa
   #20 = Utf8               Ljava/lang/String;
   #21 = Utf8               SourceFile
   #22 = Utf8               TestCode.java
   #23 = NameAndType        #8:#9          // "<init>":()V
   #24 = Utf8               java/lang/String
   #25 = Utf8               bbbb
   #26 = NameAndType        #8:#30         // "<init>":(Ljava/lang/String;)V
   #27 = Utf8               cccc
   #28 = Utf8               com/code/baseCode/TestCode
   #29 = Utf8               java/lang/Object
   #30 = Utf8               (Ljava/lang/String;)V

new #2

image-20211123223808106

dup

image-20211123223942761

ldc #3

image-20211123224047739

invokespecial #4

image-20211123224141928

astore_1

image-20211123224232319

后续的操作一样,因此会出现

 String a = new String("bbbb");
 String b = new String("bbbb");
 System.out.println(a == b); // false

面试题

做下面的题是,需要知道几个知识点

1.编辑时,编译器会对确切的值进行优化:

比如String a = "a" + "b"; -> 比如String a = "ab";

final String a = "a"; String b = "a" + a; -> String b = "aa";

2.字符串变量相加会创建StringBuilder调用append方法,然后StringBuilder转换成new String

3.intern:Java查找常量池中是否有相同Unicode的字符串常量,如果有,则返回其的引用,如果没有,则在常量池中增加一个Unicode等于str的字符串并返回它的引用。

 String s11 = "a";
 String s22 = "bc";
 ​
 String s1 = "abc";
 String s2 = "a" + "bc";
 String s3 = new String("a") + "bc";
 String s4 = new String("a") + new String("bc");
 String s5 = new String("abc");
 String s6 = s11 + s22;
 ​
 System.out.println(s1 == s2);
 System.out.println(s1 == s3);
 System.out.println(s1 == s4);
 System.out.println(s1 == s5);
 System.out.println(s1 == s6);
 System.out.println(s3 == s4);
 System.out.println(s4 == s5);
 ​
 System.out.println(s1 == s5.intern());
 System.out.println(s1 == s6.intern());
 ​
 System.out.println(s1.equals(s5));
 System.out.println(s1.equals(s6));

答案解释:[java面试题]判断字符串相等,看这一篇就够了 - 知乎

结尾

之前出了int类型的情况,上述是String类型的情况,后续出包装类型的情况

  • 5
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值