修正前:new出來的對象,會在堆中存放真正的值; 大錯特錯!!!!java
修正後:new出來的對象,堆存放的並非真正的值,而是常量池中字符串常量的地址。面試
不知道你們在作面試題時是否會遇到關於String的題,記得校招時,樓主常常遇到String的題,有時候會很懵逼。先來看一個例子:sql
public class StringTest {
public static void main(String[] args) {
String str1 = "hello";
String str2 = "hello";
String str3 = "he" + "ll" + "o";
System.out.println(str1 == str2);
System.out.println(str1 == str3);
String str = "o";
String str4 = "hell" + str;
System.out.println(str1 == str4);
String str5 = new String("hello");
System.out.println(str1 == str5);
System.out.println(str4 == str5);
System.out.println(str1.equals(str5));
//intern()函數做用:直接獲取常量池中字符串常量的地址並返回。
System.out.println(str1==str5.intern());
String str6 = new String("beyond");
}
}
//輸出的結果
//true
//true
//false
//false
//false
//true
//true
複製代碼
你答對了嗎?app
首先咱們要知道,String是Java的一個類,有兩種建立對象的方式【不考慮反射、clone、反序列化】。函數
(我這個圖畫得太醜了。。。。。。[攤手])工具
經過方式一建立String對象,會先查看常量池中是否有這個字符串常量,若是存在,則直接將引用指向這個常量(即例子中:str1==str2爲true)。比較疑惑的會是str3,str4。由於str3由字符串常量「he」、"ll"、「o」組成,編譯器發現這三個常量組成的字符串常量已經存在了常量池中,即編譯期間就已經肯定了最終值,則也會將引用指向「hello」字符串常量。對於str4,由於編譯器不能在編譯時就肯定字符串最終的值,因此會將字符串常量「hell」存放在常量池,再在堆中生成最終的對象【引伸一下:經過「+」鏈接字符串,底層是經過new StringBuilder()的append()方法進行拼接,因此應避免在循環中使用「+」來拼接字符串,以避免建立大量垃圾對象】。ui
經過方式二而建立String對象,首先會先查看常量池中是否存在這個字符串常量,若是存在,則直接在對內存中new出新對象;不然,會先在常量池生成這個字符串常量,再在堆中生成新對象(如圖中str6)。特別注意:(new出對象的值爲常量池中這個字符串常量的地址,也就是堆中存放的都是字符串常量中的地址)spa
javap是jdk自帶的反解析工具。它的做用就是根據class字節碼文件,反解析出當前類對應的code區(彙編指令)、本地變量表、異常表和代碼行偏移量映射表、常量池等等信息。命令行
javap的用法格式:javap <options> <classes>
其中classes就是你要反編譯的class文件。
在命令行中直接輸入javap或javap -help能夠看到javap的options有以下選項:code
-help --help -? 輸出此用法消息
-version 版本信息,實際上是當前javap所在jdk的版本信息,不是class在哪一個jdk下生成的。
-v -verbose 輸出附加信息(包括行號、本地變量表,反彙編等詳細信息)
-l 輸出行號和本地變量表
-public 僅顯示公共類和成員
-protected 顯示受保護的/公共類和成員
-package 顯示程序包/受保護的/公共類 和成員 (默認)
-p -private 顯示全部類和成員
-c 對代碼進行反彙編
-s 輸出內部類型簽名
-sysinfo 顯示正在處理的類的系統信息 (路徑, 大小, 日期, MD5 散列)
-constants 顯示靜態最終常量
-classpath <path> 指定查找用戶類文件的位置
-bootclasspath <path> 覆蓋引導類文件的位置
複製代碼
好比本例子:javap -verbose StringTest
String在內存的存在形式有兩種,若是經過直接賦值的形式(方式一)會將對象直接放到常量池中【若是由幾個字符串常量拼接而成,而且在編譯時就肯定了最終的結果(即常量池中存在最終拼接而成的字符串),則直接將引用指向這個字符串常量;若是在編譯時不能肯定最終的結果,則會將最終結果在堆中生成,並其中的字符串常量會在常量池中存在】;若是經過new的方式(方式二),先在常量池生成字符串常量,再在堆中生成字符串對象,對象的值爲這個字符串常量在常量池中的地址。
小弟才疏學淺,若有錯誤,敬請指正,十分感謝。