String類相關(guān)面試題很難启具?不要方本讥,問題不大

前言:

Java中有一個(gè)String類,特別讓人傷腦筋鲁冯。因?yàn)樗梢灾苯淤x值拷沸,也可以new一下用構(gòu)造器生成對(duì)象,還可以用加號(hào)拼接……這些不同的方式到底有什么區(qū)別薯演?本文是個(gè)人學(xué)習(xí)的一些總結(jié)撞芍,也希望能用最通俗的語言讓大家明白這個(gè)類。


歡迎大家關(guān)注我的公眾號(hào) javawebkf跨扮,目前正在慢慢地將簡(jiǎn)書文章搬到公眾號(hào)序无,以后簡(jiǎn)書和公眾號(hào)文章將同步更新,且簡(jiǎn)書上的付費(fèi)文章在公眾號(hào)上將免費(fèi)衡创。


一帝嗡、字符串的創(chuàng)建:

字符串創(chuàng)建有兩種方式,分別來看看這兩種方式有何區(qū)別:
1. 字面量賦值創(chuàng)建:

String str1 = "hello";
String str2 = "hello";
String str3 = "world";

這樣創(chuàng)建字符串璃氢,首先會(huì)去常量池里找有沒有這個(gè)字符串哟玷,有就直接指向常量池的該字符串,沒有就先往常量池中添加一個(gè)一也,再指向它碗降。圖解:


圖解

2. 用new創(chuàng)建:

String str1 = new String("hello");
String str2 = new String("hello");
String str3 = new String("world");

new一個(gè)字符串時(shí),做了兩件事塘秦。首先在堆中生成了該字符串對(duì)象,然后去看常量池中有沒有該字符串动看,如果有就不管了尊剔,沒有就往常量池中添加一個(gè)。圖解:


圖解

所以當(dāng)問到“執(zhí)行上面那三行代碼創(chuàng)建了幾個(gè)對(duì)象”這樣的問題就很簡(jiǎn)單了菱皆,堆中三個(gè)常量池中兩個(gè)须误,總共是5個(gè)。

小結(jié):這兩種方式創(chuàng)建出來的仇轻,一個(gè)在堆中京痢,一個(gè)在常量池中,所以它們之間用 == 比較肯定是false篷店。

二祭椰、字符串的拼接:

字符串可以直接用加號(hào)進(jìn)行拼接臭家,但是也有幾種不同的情況。
1. 常量拼接

String str = "hello" + "world";

對(duì)于這種加號(hào)兩邊都是常量的方淤,在編譯階段就會(huì)自動(dòng)拼接钉赁,變成

String str = "helloworld";

所以就會(huì)去常量池找"helloworld",有就直接指向它携茂,沒有就在常量池創(chuàng)建再指向你踩。

2. 有final的拼接:

final String str1 = "hello";
final String str2 = "world";
String str3 = str1 + str2;

因?yàn)閒inal修飾的變量就是常量,所以在編譯期直接會(huì)變成

String str3 = "hello" + "world";

再根據(jù)常量拼接規(guī)則可知最終就變成

String str3 = "helloworld";

3. 變量和常量拼接:
變量和常量拼接的時(shí)候讳苦,底層會(huì)調(diào)用StringBuilder的append方法生成新對(duì)象带膜。

  • 情況一:
String str1 = "hello";
String str2 = str1 + "world";

str1顯然是在常量池中的,world也是在常量池中的鸳谜,然后調(diào)用append方法在堆中生成新對(duì)象"helloworld"膝藕,str2就指向堆中的"helloworld"對(duì)象。所以這兩條語句總共生成了3個(gè)對(duì)象卿堂,常量池中有"hello"和"world"束莫,堆中有"helloword"。

  • 情況二:
String str1 = new String("hello");
String str2 = str1 + "world";

首先會(huì)在堆中創(chuàng)建一個(gè)"hello"草描,再把"hello"添加到常量池览绿;然后會(huì)把"world"添加到常量池,拼接的時(shí)候穗慕,會(huì)在堆中創(chuàng)建一個(gè)"helloworld"饿敲。所以這兩條語句總共創(chuàng)建了4個(gè)對(duì)象,堆中的"hello"逛绵、"helloworld"和常量池中的"hello"怀各、"world"。

4. 變量和變量拼接:
變量和變量拼接术浪,底層也會(huì)調(diào)用StringBuilder的append方法生成新對(duì)象瓢对。

  • 情況一:
String str1 = "hello";
String str2 = "world";
String str3 = str1 + str2;

這段代碼,首先會(huì)有一個(gè)"hello"在常量池中胰苏,然后有個(gè)"world"在常量池硕蛹,第三行代碼會(huì)調(diào)用append方法,在堆中生成一個(gè)"helloworld"硕并。所以總共有3個(gè)對(duì)象法焰。

  • 情況二:
String str1 = "hello";
String str2 = new String("world");
String str3 = str1 + str2;

這段代碼,首先在常量池中搞一個(gè)"hello"倔毙,然后在堆中new一個(gè)"world"埃仪,同時(shí)把"world"也搞到常量池中去,第三步拼接就會(huì)在堆中生成一個(gè)"helloworld"陕赃。所以總共有4個(gè)對(duì)象卵蛉。

  • 情況三:
String str1 = new String("hello");
String str2 = new String("world");
String str3 = str1 + str2;

第一行代碼創(chuàng)建了兩個(gè)對(duì)象颁股,堆中一個(gè)常量池一個(gè),第二行代碼也是一樣毙玻,第三行代碼就在堆中創(chuàng)建了一個(gè)"helloworld"豌蟋。所以總共創(chuàng)建了5個(gè)對(duì)象。

三桑滩、intern方法:

1梧疲、Java 1.7以前:
JDK 1.7以前,intern方法會(huì)把對(duì)象拷貝到常量池运准』系看下面例子:

  • 例一:
 String str1 = new String("str")+new String("01");
 str1.intern();
 String str2 = "str01";
 System.out.println(str2==str1);

圖解上述代碼:


圖解

首先new String("str")會(huì)在堆中創(chuàng)建str,同時(shí)添加到常量池胁澳;new String("01")也是一樣的该互,在堆中創(chuàng)建01,同時(shí)添加到常量池韭畸;然后兩者拼接宇智,底層用的append方法,在堆中生成一個(gè)str01胰丁;然后str1.intern()随橘,就把str01拷貝到常量池了;此時(shí)運(yùn)行到String str2 = "str01"锦庸,發(fā)現(xiàn)常量池中有了机蔗,所以直接指向常量池中的str01。最終str1指向堆中的str01對(duì)象甘萧,str2指向常量池的str01對(duì)象萝嘁,所以結(jié)果是false。

  • 例二:
 String str1 = new String("str")+new String("01");
 String str2 = "str01";
 str1.intern();
 System.out.println(str2==str1);

我們將第二三行代碼調(diào)換順序扬卷,看看情況有什么不同:


圖解

換一下順序牙言,區(qū)別就在于執(zhí)行到第二行代碼的時(shí)候,常量池中就已經(jīng)有str01了怪得,所以再執(zhí)行str1.intern()的時(shí)候嬉挡,就沒有再進(jìn)行拷貝了。最終還是str1指向堆中的str01汇恤,str2指向常量池的str01,所以結(jié)果還是false拔恰。

2因谎、JDK1.7以后(包括1.7):
從JDK 1.7開始,intern方法做了些改變颜懊,進(jìn)行拷貝的時(shí)候不是拷貝對(duì)象财岔,而是拷貝地址值风皿。看下面的例子:

  • 例一:
String str1 = new String("str")+new String("01");
str1.intern();
String str2 = "str01";
System.out.println(str2==str1);

圖解上述代碼:

圖解

第一步和JDK 1.7之前是一樣的匠璧,現(xiàn)在堆中創(chuàng)建一個(gè)str桐款,同時(shí)搞到常量池,再創(chuàng)建一個(gè)01夷恍,同時(shí)搞到常量池魔眨,然后拼接,在堆中生成對(duì)象str01酿雪;不同的就是str1.intern()遏暴,這次拷貝的不是str01這個(gè)對(duì)象,而是把它的地址值搞到常量池中去了指黎;然后執(zhí)行String str2 = str01的時(shí)候朋凉,去常量池找str01,發(fā)現(xiàn)常量池中有x001地址值醋安,剛好該地址值對(duì)應(yīng)的就是要找的str01杂彭,就直接拿過來用。最終就是str1指向地址值為x001的對(duì)象吓揪,str2也是指向地址值為x001的對(duì)象亲怠,所以結(jié)果是true。

  • 例二:
String str1 = new String("str")+new String("01");
String str2 = "str01";
str1.intern();
System.out.println(str2==str1);

同樣將二三行代碼換一下位置磺芭,看看是什么情況:

圖解

第一步就不多說了赁炎,執(zhí)行第二步時(shí),往常量池中找str01钾腺,發(fā)現(xiàn)沒有徙垫,那就添加一個(gè);再執(zhí)行str1.intern()時(shí)放棒,發(fā)現(xiàn)常量池中有str01了姻报,就不進(jìn)行地址值的拷貝了。最終str1指向堆中的str01间螟,str2指向常量池的str01吴旋,所以結(jié)果是false。

  • 例三:
String str1 = new String("str")+new String("01");
String str2 = "str01";
str1 = str1.intern();
System.out.println(str2==str1);

就是把例二的str1.intern()改成str1 = str1.intern()厢破,看看會(huì)有什么變化:

圖解

本來str1是指向堆中的str01的荣瑟,然后重新將str1.intern()賦給str1,str1.intern()是指向常量池的摩泪,賦給str1后笆焰,所以此時(shí)str1也是指向常量池。所以結(jié)果就是true见坑。

四嚷掠、String捏检、StringBuilder和StringBuffer:

String和后兩者的區(qū)別就是String是不可變的,后兩者可變不皆。StringBuilder是JDK 1.5以后提供的贯城,以前用StringBuffer。StringBuffer和StringBuilder的功能基本一樣霹娄,只是StringBuffer是線程安全的能犯,而StringBuilder不是線程安全的。因此项棠,StringBuilder的效率會(huì)更高悲雳。

上面字符串拼接部分的案例都是用加號(hào)拼接的,然后也提到了StringBuilder的append方法香追。其實(shí)就算是加號(hào)拼接合瓢,底層還是用的StringBuilder的append方法⊥傅洌看下面代碼:

String s = "abc"; 
String ss = "ok" + s + "xyz" + 5; 

這就用加號(hào)拼接的例子晴楔,利用反編譯工具看看這段代碼到底編譯成了啥:

String s = "abc";
String ss = (new StringBuilder("ok")).append(s).append("xyz").append(5).toString(); 

可看到,編譯后是用StringBuilder的append方法進(jìn)行拼接的峭咒。那么使用加號(hào)和使用append方法到底有什么區(qū)別呢税弃?看一下以下代碼:

String s = ""; 
Random rand = new Random(); 
for (int i = 0; i < 10; i++){
     s = s + rand.nextInt(1000) + " "; 
} 
System.out.println(s);

這個(gè)例子很簡(jiǎn)單,就是在循環(huán)里面用加號(hào)進(jìn)行字符串的拼接凑队,看一下反編譯后是什么樣子的:

String s = ""; 
Random rand = new Random(); 
for(int i = 0; i < 10; i++) {
    s = (new StringBuilder(String.valueOf(s))).append(rand.nextInt(1000)).append(" ").toString(); 
}
System.out.println(s); 

可以看到则果,它是在循環(huán)里面new了StringBuilder對(duì)象,然后用其append方法進(jìn)行拼接漩氨。這里是i從0到9西壮,也就是說要new十次,會(huì)創(chuàng)建十個(gè)對(duì)象叫惊,這樣就會(huì)占用大量的資源款青。所以要讓其編譯后創(chuàng)建StringBuilder對(duì)象的過程在循環(huán)外面,代碼就該這樣寫:

String s = ""; 
Random rand = new Random();
StringBuilder result = new StringBuilder();
for (int i = 0; i < 10; i++){ 
      result.append(rand.nextInt(1000)); 
      result.append(" ");
} 
System.out.println(result.toString()); 

那么編譯后就是這樣的:

String s = ""; 
Random rand = new Random(); 
StringBuilder result = new StringBuilder(); 
for(int i = 0; i < 10; i++) {
    result.append(rand.nextInt(1000)); 
    result.append(" "); 
}
System.out.println(result.toString()); 

這樣就沒有在循環(huán)里面new對(duì)象了霍狰。
小結(jié):當(dāng)要在循環(huán)里面進(jìn)行字符串拼接的時(shí)候抡草,就該先在循環(huán)外面new一個(gè)StringBuilder,然后在循環(huán)里面用append進(jìn)行拼接蔗坯;其他情況就可以使用加號(hào)進(jìn)行拼接更加簡(jiǎn)單。

總結(jié):

本文用圖文形式講了String的面試考點(diǎn)宾濒,特別要注意JDK版本不同intern方法的差異。還有就是常量池的位置到底在方法區(qū)還是在堆中還是在元空間,這個(gè)我也不是很清楚答姥,網(wǎng)上搜索的答案也比較雜谚咬。以上內(nèi)容如果有誤,歡迎批評(píng)指正择卦!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市秉继,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌尚辑,老刑警劉巖辑鲤,帶你破解...
    沈念sama閱讀 210,914評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異杠茬,居然都是意外死亡月褥,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評(píng)論 2 383
  • 文/潘曉璐 我一進(jìn)店門瓢喉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來宁赤,“玉大人,你說我怎么就攤上這事栓票【鲎螅” “怎么了?”我有些...
    開封第一講書人閱讀 156,531評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵走贪,是天一觀的道長(zhǎng)佛猛。 經(jīng)常有香客問我,道長(zhǎng)厉斟,這世上最難降的妖魔是什么挚躯? 我笑而不...
    開封第一講書人閱讀 56,309評(píng)論 1 282
  • 正文 為了忘掉前任,我火速辦了婚禮擦秽,結(jié)果婚禮上码荔,老公的妹妹穿的比我還像新娘。我一直安慰自己感挥,他們只是感情好缩搅,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評(píng)論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著触幼,像睡著了一般硼瓣。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,730評(píng)論 1 289
  • 那天堂鲤,我揣著相機(jī)與錄音亿傅,去河邊找鬼。 笑死瘟栖,一個(gè)胖子當(dāng)著我的面吹牛葵擎,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播半哟,決...
    沈念sama閱讀 38,882評(píng)論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼酬滤,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了寓涨?” 一聲冷哼從身側(cè)響起盯串,我...
    開封第一講書人閱讀 37,643評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎戒良,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蔬墩,經(jīng)...
    沈念sama閱讀 44,095評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡拇颅,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評(píng)論 2 325
  • 正文 我和宋清朗相戀三年樟插,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了黄锤。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評(píng)論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡副编,死狀恐怖痹届,靈堂內(nèi)的尸體忽然破棺而出打月,到底是詐尸還是另有隱情,我是刑警寧澤柴淘,帶...
    沈念sama閱讀 34,253評(píng)論 4 328
  • 正文 年R本政府宣布为严,位于F島的核電站,受9級(jí)特大地震影響荸型,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜稿静,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評(píng)論 3 312
  • 文/蒙蒙 一改备、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧盐捷,春花似錦默勾、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炫隶。三九已至,卻和暖如春煞檩,著一層夾襖步出監(jiān)牢的瞬間望门,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評(píng)論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人友存。 一個(gè)月前我還...
    沈念sama閱讀 46,248評(píng)論 2 360
  • 正文 我出身青樓屡立,卻偏偏與公主長(zhǎng)得像搀军,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子焚刺,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • 從網(wǎng)上復(fù)制的乳愉,看別人的比較全面屯远,自己搬過來,方便以后查找坡脐。原鏈接:https://www.cnblogs.com/...
    lxtyp閱讀 1,344評(píng)論 0 9
  • 前言 RTFSC (Read the fucking source code )才是生活中最重要的挨措。我們天天就是要...
    二毛_coder閱讀 451評(píng)論 1 1
  • String 的聲明 由 JDK 中關(guān)于String的聲明可以知道: 不同字符串可能共享同一個(gè)底層char數(shù)組浅役,例...
    CodeKing2017閱讀 1,619評(píng)論 1 2
  • 落地實(shí)修第15天 我偶爾會(huì)上山觉既,看著大家跳舞乳幸。在外圍練腳步》罚看似簡(jiǎn)單的走步瓶埋,三步一抬诊沪,學(xué)起來也不容易端姚。經(jīng)常還...
    陳QX閱讀 111評(píng)論 0 0
  • 今天是六一兒童節(jié)挤悉,是孩子們的節(jié)日,早上都沒用我叫昏鹃,子屹就起床了诀诊,我說,你不困嗎?他搖搖頭說不奈懒。吃了早飯,送他去上學(xué)...
    知足常樂_3afa閱讀 184評(píng)論 0 0