經典面試題 String str = new String(“hello world”);建立了幾個對象。
學習 java 準備面試,不能光背面試題的答案,更要深入面試題,了解面試題背後的java基礎。
那麼讓我們來看一下
String str = new String("hello world");
這段代碼到底涉及到了多少 java 基礎。
我知道的建立 String 對象的方式共有五種。
-
new String(char[ ])
通過 char 數組建立 String 對象
-
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
- new String(int[ ])
-
最常用的字面量形式建立String對象String str = "123";
-
String str = new String("123");
本節隻涉及 4、5 兩種建立 String 對象的方式。
1.字面量形式建立
String str = "123";
我們稱 123 為字面量,當你點選編譯軟體的運作按鈕,目前 .java 檔案會被編譯為 .class 位元組碼檔案。字面量 123 存在于 class 檔案的常量池中。其中,常量池中會存在兩個與 123 相關的常量。
- CONSTANT_Utf8_info 就是 123
- CONSTANT_String_info 存儲 CONSTANT_Utf8_info 的索引
當這個類被加載時,常量池會被加載進入運作時常量池,以上兩個常量的類型會發生變化。
- CONSTANT_Utf8_info -> Symbol* ,指針指向 C++ 類型的 Symbol 對象,symbol 對象的值就是 123
- 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 基礎
- String 的幾種建立形式
- .class 檔案的常量池
- 類的加載階段會将 class 檔案中的常量池加載進入運作時常量池中,并且改變常量的結構
- ldc 等操作符号引用的位元組碼指令會觸發類的解析階段
- 類的解析階段會将符号引用替換為直接引用,本文涉及到 String 對象的建立
- 字元串常量池
-
會在運作時在堆中建立 String 對象,這個對象是野生的不歸字元串常量池管理new String("123");
想更了解字元串常量池的同學,可以看我上一篇部落格,深入Java基礎——字元串常量池,感謝捧場。
如果發現本文有錯誤請直接指出,感謝。