溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Java中字符串常量池的示例分析

發布時間:2021-08-30 09:14:25 來源:億速云 閱讀:111 作者:小新 欄目:開發技術

這篇文章給大家分享的是有關Java中字符串常量池的示例分析的內容。小編覺得挺實用的,因此分享給大家做個參考,一起跟隨小編過來看看吧。

常量池

把經常用到的數據存放在某塊內存中,避免頻繁的數據創建與銷毀,實現數據共享,提高系統性能。

八種基礎數據類型除了float和double都實現了常量池技術。在近代的JDK版本中(1.7后),字符串常量池被實現在Java堆內存中。

下面通過三行代碼讓大家對字符串常量池建立初步認識:

public static void main(String[] args) {
    String s1 = "hello";
    String s2 = new String("hello");
    System.out.println(s1 == s2);   //false
}

先來看看第一行代碼String s1 = "hello";

Java中字符串常量池的示例分析

直接通過雙引號( String s1 = "hello")聲明字符串的方式,虛擬機首先會到字符串常量池中查找該字符串是否已經存在。如果存在會直接返回該引用,如果不存在則會在堆內存中創建該字符串對象,然后到字符串常量池中注冊該字符串。

上面的代碼中( String s1 = "hello")虛擬機首先會到字符串常量池中查找是否有存在hello字符串對應的引用。發現沒有后會在堆內存創建hello字符串對象(內存地址0x0001),然后到字符串常量池中注冊地址為0x0001的hello對象,也就是添加指向0x0001的引用。最后把字符串對象返回給s1。

下面看String s2 = new String("hello");

Java中字符串常量池的示例分析

當我們使用new關鍵字創建字符串對象的時候,JVM將不會查詢字符串常量池,它將會直接在堆內存中創建一個字符串對象,并返回給所屬變量。

所以s1和s2指向的是兩個完全不同的對象,判斷s1 == s2的時候會返回false。

再來看下面的示例:

public static void main(String[] args) {
    String s1 = new String("hello ") + new String("world");
    s1.intern();
    String s2 = "hello world";
    System.out.println(s1 == s2);   //true
}

第一行代碼String s1 = new String("hello ") + new String("world");的執行過程是這樣子的:

  1. 依次在堆內存中創建hello和world兩個字符串對象;

  2. 然后把它們拼接起來 (底層使用StringBuilder實現);

  3. 在拼接完成后會產生新的hello world對象,這時變量s1指向新對象hello world。

執行完第一行代碼后,內存是這樣子的:

Java中字符串常量池的示例分析

第二行代碼s1.intern();

當調用intern()方法時,首先會去常量池中查找是否有該字符串對應的引用,如果有就直接返回該字符串;

如果沒有,就會在常量池中注冊該字符串的引用,然后返回該字符串。

由于第一行代碼采用的是new的方式創建字符串,所以在字符串常量池中沒有保存hello world對應的引用,虛擬機會在常量池中進行注冊,注冊完后的內存示意圖如下:

Java中字符串常量池的示例分析

第三行代碼String s2 = "hello world";

首先虛擬機會去檢查字符串常量池,發現有指向hello world的引用。然后把該引用所指向的字符串直接返回給所屬變量。

執行完第三行代碼后,內存示意圖如下:

Java中字符串常量池的示例分析

如圖所示,s1和s2指向的是相同的對象,所以當判斷s1 == s2時返回true。

總結:

  • 當用new關鍵字創建字符串對象時,不會查詢字符串常量池;

  • 當用雙引號直接聲明字符串對象時,虛擬機將會查詢字符串常量池。

說白了就是:字符串常量池提供了字符串的復用功能,除非我們要顯式創建新的字符串對象,否則對同一個字符串虛擬機只會維護一份拷貝。

反編譯代碼驗證字符串初始化操作

下面我們再來看一個示例:

public class Main {
    public static void main(String[] args) {
        String s1 = "hello ";
        String s2 = "world";
        String s3 = s1 + s2;
        String s4 = "hello world";
        System.out.println(s3 == s4);
    }
}

首先第一行和第二行是常規的字符串對象聲明,它們分別會在堆內存創建字符串對象,并會在字符串常量池中進行注冊。

影響我們做出判斷的是第三行代碼String s3 = s1 + s2;,我們不知道s1 + s2在創建完新字符串hello world后是否會在字符串常量池進行注冊。

簡單點說:我們不知道這行代碼是以雙引號形式聲明字符串,還是用new關鍵字創建字符串。

那么我們看下這端代碼的反編譯后的代碼:

PS D:\code\javaSE\target\classes\demo> javap -c .\Main.class
Compiled from "Main.java"
public class demo.Main {
  public demo.Main();
    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 hello
       2: astore_1
       3: ldc           #3                  // String world
       5: astore_2
       6: new           #4                  // class java/lang/StringBuilder
       9: dup
      10: invokespecial #5                  // Method java/lang/StringBuilder."<init>":()V
      13: aload_1
      14: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      17: aload_2
      18: invokevirtual #6                  // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      21: invokevirtual #7                  // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      24: astore_3
      25: ldc           #8                  // String hello world
      27: astore        4
      29: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
      32: aload_3
      33: aload         4
      35: if_acmpne     42
      38: iconst_1
      39: goto          43
      42: iconst_0
      43: invokevirtual #10                 // Method java/io/PrintStream.println:(Z)V
      46: return
}

直接看重點:

  • 21: invokevirtual #7 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;

  • 24: astore_3

  • 虛擬機調用StringBuilder的toString()方法獲得字符串hello world,并存放至s3。

下面是我們追蹤StringBuilder的toString()方法源碼:

@Override
public String toString() {
    // Create a copy, don't share the array
    return new String(value, 0, count);
}

通過以上源碼可以看出:s3是通過new關鍵字獲得字符串對象的。

回到題目,也就是說字符串常量表中沒有存儲hello world的引用,當s4以引號的形式聲明字符串時,由于在字符串常量池中查不到相應的引用,所以會在堆內存中新創建一個字符串對象。 所以s3和s4指向的不是同一個字符串對象, 結果為false。

感謝各位的閱讀!關于“Java中字符串常量池的示例分析”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,讓大家可以學到更多知識,如果覺得文章不錯,可以把它分享出去讓更多的人看到吧!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女