天天看點

深入經典面試題:new String(“123“) 建立了幾個String對象

經典面試題 String str = new String(“hello world”);建立了幾個對象。

學習 java 準備面試,不能光背面試題的答案,更要深入面試題,了解面試題背後的java基礎。

那麼讓我們來看一下

String str = new String("hello world");

這段代碼到底涉及到了多少 java 基礎。

我知道的建立 String 對象的方式共有五種。

  1. new String(char[ ])

    通過 char 數組建立 String 對象

  2. new String(byte[ ])

    通過 byte 數組建立 String 對象。因為 JDK1.8 中,String 中的資料是由 char[ ]存儲的,當通過 byte 數組建立 String 對象時,需要将 byte 數組轉換為 char 數組。由于 byte 是一個位元組,而 char 是兩個位元組,而且 byte 的資料範圍包含負數。是以兩個數組互相轉換時,如果不指定編碼方式,可能會導緻亂碼 (java 中 char 為 unicode 編碼,而以 byte[ ] 形式建立 String 對象通常是網絡請求或者IO讀入時使用的,包含多種編碼方式),以及 byte 數組前後不一緻的錯誤。

    String str = new String(new byte[]{-24,97,98,99});
    System.out.println(str);
    for (byte aByte : str.getBytes()) {
        System.out.print(aByte + " ");
    }
    
    //output
    �abc
    -17 -65 -67 97 98 99 !!前後 byte 數組不一緻
               
    需要指定編碼方式例如:
    String str = new String(new byte[]{-24,97,98,99},"ISO-8859-1");
    System.out.println(str);
    for (byte aByte : str.getBytes("ISO-8859-1")) {
        System.out.print(aByte + " ");
    }
    
    //output
    èabc
    -24 97 98 99 
               
  3. new String(int[ ])
  4. String str = "123";

    最常用的字面量形式建立String對象
  5. String str = new String("123");

本節隻涉及 4、5 兩種建立 String 對象的方式。

1.字面量形式建立

String str = "123";

我們稱 123 為字面量,當你點選編譯軟體的運作按鈕,目前 .java 檔案會被編譯為 .class 位元組碼檔案。字面量 123 存在于 class 檔案的常量池中。其中,常量池中會存在兩個與 123 相關的常量。

  1. CONSTANT_Utf8_info 就是 123
  2. CONSTANT_String_info 存儲 CONSTANT_Utf8_info 的索引

當這個類被加載時,常量池會被加載進入運作時常量池,以上兩個常量的類型會發生變化。

  1. CONSTANT_Utf8_info -> Symbol* ,指針指向 C++ 類型的 Symbol 對象,symbol 對象的值就是 123
  2. CONSTANT_String_info -> JVM_CONSTANT_UnresolveString_info(未被解析的 String 引用)

在加載階段不會産生 String 對象。直到解析階段才會建立 String 對象。當虛拟機運作到 ldc 等操作符号引用的位元組碼指令時,會觸發這個類的解析階段,将符号引用替換為直接引用。而此時 JVM_CONSTANT_UnresolveString_info 也就會被 resolve 為真正的 String 對象的引用。當然此時需要在堆中建立内容為 123 的 String 對象,但也不是傻傻的建立對象。建立時會先去判斷字元串常量池中是否有内容一樣也是 123 的對象的引用。如果有則不需要建立新的 String 對象。

至此,第四種建立 String 的方式講解完成,至于建立時如何判斷,以及如何向字元串常量池中添加新的資料,涉及到字元串常量池的實作原理 StringTable,筆者能力不夠,暫不講解。有興趣的老鐵可以去閱讀 StringTable 源碼(查詢方法 StringTable::lockup(),添加方法 StringTable::basic_add() )。

總結:通過字面量形式建立 String 對象,對象會在類的解析階段被建立,并且相同内容的對象隻有一個。如果建立時已經存在重複的,則不會建立新的 String 對象。

2.有參構造建立 String 對象

String str = new String("123");

同樣, 123 是字面量,在類的解析階段會建立 String 對象。與第四種建立方式不同的地方是,使用了 String 的有參構造。

當使用構造方法建立 String 對象,不光會在類加載過程中建立,還會在程式運作時在堆中建立 String 對象。并且這個對象是野生的,不歸字元串常量池管。

隻有在類加載過程中産生的 String 對象,以及運作時通過調用 String::intern() 方法把 String 的監護權給字元串常量池的 String 對象,才歸字元串常量池管。不太懂?那反過來想,如果所有 String 對象都歸字元串常量池管的話,在程式運作期間,可能會出現無數個 String,那字元串常量池早就炸了。不然為什麼叫字元串常量池,而不叫字元串池。

3.總結

這個面試題,涉及到的 java 基礎

  1. String 的幾種建立形式
  2. .class 檔案的常量池
  3. 類的加載階段會将 class 檔案中的常量池加載進入運作時常量池中,并且改變常量的結構
  4. ldc 等操作符号引用的位元組碼指令會觸發類的解析階段
  5. 類的解析階段會将符号引用替換為直接引用,本文涉及到 String 對象的建立
  6. 字元串常量池
  7. new String("123");

    會在運作時在堆中建立 String 對象,這個對象是野生的不歸字元串常量池管理

想更了解字元串常量池的同學,可以看我上一篇部落格,深入Java基礎——字元串常量池,感謝捧場。

如果發現本文有錯誤請直接指出,感謝。