常量池和局部變量表(四)

這裏都是針對jdk1.8的hotspot虛擬機講解 

public class Demo1_1{
    public static void main(String[] args) {
        String s1 = "a";
        String s2 = "b";
        String s3 = "ab";
        String s4 = s1+s2;
        String s5 = "a" + "b";
        
    }
}

 

通過在命令窗口反編譯class文件得到如下信息: 認識二進制字節碼文件內容(三)
 

D:\workspace\day01easyjdbc01\target\classes\com\demo\jvm>javap -v Demo1_1.class
Classfile /D:/workspace/day01easyjdbc01/target/classes/com/demo/jvm/Demo1_1.class
  Last modified 2020-3-29; size 706 bytes
  MD5 checksum 0ba87b6925bb2d842174a3e3f4a1c3df
  Compiled from "Demo1_1.java"
public class com.demo.jvm.Demo1_1
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #10.#30        // java/lang/Object."<init>":()V
   #2 = String             #31            // a
   #3 = String             #32            // b
   #4 = String             #33            // ab
   #5 = Class              #34            // java/lang/StringBuilder
   #6 = Methodref          #5.#30         // java/lang/StringBuilder."<init>":()V
   #7 = Methodref          #5.#35         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #8 = Methodref          #5.#36         // java/lang/StringBuilder.toString:()Ljava/lang/String;
   #9 = Class              #37            // com/demo/jvm/Demo1_1
  #10 = Class              #38            // java/lang/Object
  #11 = Utf8               <init>
  #12 = Utf8               ()V
  #13 = Utf8               Code
  #14 = Utf8               LineNumberTable
  #15 = Utf8               LocalVariableTable
  #16 = Utf8               this
  #17 = Utf8               Lcom/demo/jvm/Demo1_1;
  #18 = Utf8               main
  #19 = Utf8               ([Ljava/lang/String;)V
  #20 = Utf8               args
  #21 = Utf8               [Ljava/lang/String;
  #22 = Utf8               s1
  #23 = Utf8               Ljava/lang/String;
  #24 = Utf8               s2
  #25 = Utf8               s3
  #26 = Utf8               s4
  #27 = Utf8               s5
  #28 = Utf8               SourceFile
  #29 = Utf8               Demo1_1.java
  #30 = NameAndType        #11:#12        // "<init>":()V
  #31 = Utf8               a
  #32 = Utf8               b
  #33 = Utf8               ab
  #34 = Utf8               java/lang/StringBuilder
  #35 = NameAndType        #39:#40        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #36 = NameAndType        #41:#42        // toString:()Ljava/lang/String;
  #37 = Utf8               com/demo/jvm/Demo1_1
  #38 = Utf8               java/lang/Object
  #39 = Utf8               append
  #40 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #41 = Utf8               toString
  #42 = Utf8               ()Ljava/lang/String;
{
  public com.demo.jvm.Demo1_1();
    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 3: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   Lcom/demo/jvm/Demo1_1;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=6, args_size=1
         0: ldc           #2                  // String a
         2: astore_1
         3: ldc           #3                  // String b
         5: astore_2
         6: ldc           #4                  // String ab
         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: invokevirtual #8                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        27: astore        4
        29: ldc           #4                  // String ab
        31: astore        5
        33: return
      LineNumberTable:
        line 5: 0
        line 6: 3
        line 7: 6
        line 8: 9
        line 9: 29
        line 10: 33
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0      34     0  args   [Ljava/lang/String;
            3      31     1    s1   Ljava/lang/String;
            6      28     2    s2   Ljava/lang/String;
            9      25     3    s3   Ljava/lang/String;
           29       5     4    s4   Ljava/lang/String;
           33       1     5    s5   Ljava/lang/String;
}
SourceFile: "Demo1_1.java"
1. String s1 = "a"  代碼說明

根據虛擬機指令0,解釋器執行了運行時常量池裏面的#2,這個時候常量池從空值狀態變成了存放了一個符號a(這裏只是一個符號),然後根據#31 加載字符串對象"a",常量"a" 壓入操作棧

接着執行虛擬機指令2,執行astore_1把剛纔的字符串對象"a"保存到LocalVariableTable局部變量表中Slot位置1,變量名是s1

最後就是jvm棧中變量名爲s1的指向堆內存中的常量池裏面的"a"

同理 String s2 = "b" 

最後把字符串對象"b"保存到局部變量表的中位置2,變量名爲s2.

2.接下來在看看String s3 = "ab" 代碼說明:

這裏執行虛擬機指令9,調用new關鍵字 最後在堆空間創建了一個StringBuilder對象。

執行指令13,調用了init的無參構造函數

執行指令16,調用aload_1,從局部變量表中拿到s1,

執行指令17,調用StringBuilder.append方法,把s1傳入方法中,

同理執行20.21,把s2傳入方法中

執行24,調用StrinbBuilder.toString方法

執行27,執行 astore 4 ,把toString 轉換後的結果存入局部變量表中的4號位置。

通過查看StringBuilder源代碼中toString方法,也可以看出是new了一個新的字符串對象。

3.接着看String s5 = "a" + "b"說明

執指令行29,找到的常量池中#4,也就是符號ab,也就是說常量池中如果已經存在要找的字符串,不需要在存放,直接拿來使用即可

執行指令31,存放到局部變量表中位置5

執行33,方法結束

 

小知識點:可以通過調用intern()方法,嘗試講字符串放入常量池中,有則放入,並返回串池中的對象

1.6版本中,如果串池中沒有當前放入的常量,則是copy一份放入串池的

String a1 = "a" + "b" ;
String a2 = a.intern();

總結

  • 字符串對象是不可變(包裝類也如此)
  • 第一次使用常量池中的符號才變成對象
  • 字符串變量拼接 原理是StringBuilder.append(),編譯期間無法確定字符串變量引用的值,運行期才能確定
  • 字符串常量是編譯期間就能確定對象
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章