new String()究竟創(chuàng)建幾個(gè)對(duì)象?
1. 由來
遇到一個(gè)Java面試題道媚,是關(guān)于String
的辽剧,自己對(duì)String
還有點(diǎn)研究?下面是題目的描述:
在Java中辫红,
new String("hello")
這樣的創(chuàng)建方式晴裹,到底創(chuàng)建了幾個(gè)String
對(duì)象被济?
題目下答案,各說紛紜涧团,有說1個(gè)的只磷,有說2個(gè)的。我覺得都對(duì)泌绣,但也都不對(duì)钮追,因?yàn)橐由弦欢ǖ臈l件,下面來分析下阿迈!
2. 解答
2.1. 分析
題目中的String
創(chuàng)建方式元媚,是調(diào)用String
的有參構(gòu)造函數(shù),而這個(gè)有參構(gòu)造函數(shù)的源碼則是這樣的public String(String original)
苗沧,這就是說刊棕,我們可以把代碼轉(zhuǎn)換為下面這種:
String temp = "hello"; // 在常量池中
String str = new String(temp); // 在堆上
這段代碼就創(chuàng)建了2個(gè)String
對(duì)象,temp
指向在常量池中的待逞,str
指向堆上的甥角,而str
內(nèi)部的char value[]
則指向常量池中的char value[]
,所以這里的答案是2個(gè)對(duì)象识樱。(這里不再詳述內(nèi)部過程嗤无,之前的文章有寫,參考深入淺出Java String)
那之前我為什么說答案是1個(gè)的也對(duì)呢怜庸,假如就只有這一句String str = new String("hello")
代碼当犯,并且此時(shí)的常量池的沒有"hello"
這個(gè)String,那么答案是兩個(gè);如果此時(shí)常量池中休雌,已經(jīng)存在了"hello"
灶壶,那么此時(shí)就只創(chuàng)建堆上str
,而不會(huì)創(chuàng)建常量池中temp
,(注意這里都是引用)杈曲,所以此時(shí)答案就是1個(gè)驰凛。
當(dāng)然,光說不練假把式担扑,下面就來用例子驗(yàn)證一下:
2.2. 驗(yàn)證
不啰嗦恰响,直接看第一段代碼:
// 代碼1
public static void main(String[] args) {
String str = new String("hello");
String after_str = "hello";
System.out.println(str + after_str);
}
我們?cè)?code>println語句那里加個(gè)斷點(diǎn),debug
下涌献,如下圖:
[圖片上傳失敗...(image-a19eb5-1613797688333)]
可以看到在代碼1中胚宦,str
和after_str
不是同一個(gè)對(duì)象,這個(gè)應(yīng)該沒有疑問把燕垃,一個(gè)在堆上枢劝,一個(gè)常量池中,但是我們注意到str.value
和after_str.value
的地址確是同一個(gè),即是同一個(gè)char
數(shù)組卜壕,所以可以理解為您旁,當(dāng)執(zhí)行str
那句代碼時(shí),創(chuàng)建了兩個(gè)String
對(duì)象轴捎,一個(gè)在常量池鹤盒,一個(gè)在堆上,接下來到執(zhí)行after_str
這句時(shí)侦副,這種方式創(chuàng)建的字符串都在常量池中侦锯,但是如果常量中如果有,就直接返回了秦驯,所以這里返回的是上一句在常量池那里創(chuàng)建的String
,所以str
這句代碼創(chuàng)建的是2個(gè)對(duì)象尺碰。
接下來再看一段代碼:
// 代碼2
public static void main(String[] args) {
String before_str = "hello";
String str = new String("hello");
System.out.println(str + before_str);
}
同樣在println
打斷點(diǎn),如下圖:
[圖片上傳失敗...(image-52383e-1613797688333)]
在代碼2中译隘,str.value
和before_str.value
也是用的同一個(gè)char
數(shù)組葱蝗,但這里的代碼可不是和代碼1相同的,這里我們把"hello"
提到前面創(chuàng)建细燎,這也就是說明當(dāng)執(zhí)行str
那句代碼時(shí)两曼,沒有再在常量池中創(chuàng)建"hello"
了,所以str
這句則是創(chuàng)建了1個(gè)對(duì)象玻驻。
2.3. 補(bǔ)充
這里突然想到悼凑,還要補(bǔ)充一下上面如果常量池中沒有,則創(chuàng)建2個(gè)對(duì)象
,很多人會(huì)誤解這句話的含義璧瞬,這個(gè)常量池户辫,不是你沒手動(dòng)的創(chuàng)建,常量池中就沒有東西嗤锉,這個(gè)理解是錯(cuò)誤的渔欢,大家一定要修正它。在你的程序啟動(dòng)時(shí)瘟忱,就已經(jīng)裝載來很多class
奥额,這里面也有字符常量的創(chuàng)建苫幢,只是不是你自己創(chuàng)建的,你只是不知道而已垫挨。
例如下面這段代碼:
// 代碼3韩肝,JDK >= 1.7
public static void main(String[] args) {
String str1 = new StringBuilder("hel").append("lo").toString();
String str2 = new StringBuilder("ja").append("va").toString();
System.out.println(str1.intern() == str1); // true
System.out.println(str2.intern() == str2); // false
}
(關(guān)于intern
在不同JDK的不同表現(xiàn),之前也說過了九榔,詳情請(qǐng)看深入淺出Java String)
代碼3這里哀峻,str1
的結(jié)果是很多人都能理解的,但是str2
的結(jié)果為false
就說明哲泊,常量池中已經(jīng)有了"java"
了剩蟀,這里很多人不解,這個(gè)就是系統(tǒng)裝入的字符常量切威,除了"java"
育特,類似的還有"false"
,"true"
(在java.lang.String
里)等等。
注:System類里面有一行代碼 sun.misc.Version.init();Version類里面有一個(gè)靜態(tài)常量字符串launcher_name=“java”
3. 總結(jié)
通過這個(gè)面試題牢屋,可以看到String
真的是有很多”坑”且预,但是弄明白這些”坑”也是很有趣的。還有烙无,這些都是我自己找資料锋谐,實(shí)踐,總結(jié)截酷,分析出來的涮拗,難免有錯(cuò)誤存在,有發(fā)現(xiàn)的錯(cuò)誤的同學(xué)迂苛,還請(qǐng)指正三热!