String問題

Java中有一個String類,特別讓人傷腦筋。因為它可以直接賦值音瓷,也可以new一下用構(gòu)造器生成對象,還可以用加號拼接……這些不同的方式到底有什么區(qū)別夹抗?本文是個人學習的一些總結(jié),也希望能用最通俗的語言讓大家明白這個類纵竖。

一漠烧、字符串的創(chuàng)建

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

1. 字面量賦值創(chuàng)建:

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

這樣創(chuàng)建字符串靡砌,首先會去常量池里找有沒有這個字符串已脓,有就直接指向常量池的該字符串,沒有就先往常量池中添加一個通殃,再指向它度液。圖解:

image.png

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

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

new一個字符串時,做了兩件事画舌。首先在堆中生成了該字符串對象堕担,然后去看常量池中有沒有該字符串,如果有就不管了曲聂,沒有就往常量池中添加一個霹购。圖解:


image.png

所以當問到“執(zhí)行上面那三行代碼創(chuàng)建了幾個對象”這樣的問題就很簡單了,堆中三個常量池中兩個朋腋,總共是5個齐疙。
小結(jié):這兩種方式創(chuàng)建出來的,一個在堆中旭咽,一個在常量池中贞奋,所以它們之間用 == 比較肯定是false。

二穷绵、字符串的拼接:

字符串可以直接用加號進行拼接轿塔,但是也有幾種不同的情況。

1. 常量拼接

String str = "hello" + "world";

對于這種加號兩邊都是常量的请垛,在編譯階段就會自動拼接催训,變成

String str = "helloworld";

所以就會去常量池找"helloworld",有就直接指向它宗收,沒有就在常量池創(chuàng)建再指向漫拭。

2. 有final的拼接:

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

因為final修飾的變量就是常量,所以在編譯期直接會變成

String str3 = "hello" + "world";

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

String str3 = "helloworld";

3. 變量和常量拼接:
變量和常量拼接的時候混稽,底層會調(diào)用StringBuilder的append方法生成新對象采驻。

情況一:

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

情況二:

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

首先會在堆中創(chuàng)建一個"hello"审胚,再把"hello"添加到常量池;然后會把"world"添加到常量池礼旅,拼接的時候膳叨,會在堆中創(chuàng)建一個"helloworld"。所以這兩條語句總共創(chuàng)建了4個對象痘系,堆中的"hello"菲嘴、"helloworld"和常量池中的"hello"、"world"汰翠。

4. 變量和變量拼接:
變量和變量拼接龄坪,底層也會調(diào)用StringBuilder的append方法生成新對象。

情況一:

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

這段代碼复唤,首先會有一個"hello"在常量池中健田,然后有個"world"在常量池,第三行代碼會調(diào)用append方法佛纫,在堆中生成一個"helloworld"妓局。所以總共有3個對象。

情況二:

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

這段代碼呈宇,首先在常量池中搞一個"hello"好爬,然后在堆中new一個"world",同時把"world"也搞到常量池中去甥啄,第三步拼接就會在堆中生成一個"helloworld"抵拘。所以總共有4個對象。

情況三:

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

第一行代碼創(chuàng)建了兩個對象型豁,堆中一個常量池一個僵蛛,第二行代碼也是一樣,第三行代碼就在堆中創(chuàng)建了一個"helloworld"迎变。所以總共創(chuàng)建了5個對象充尉。

三、intern方法:

1衣形、Java 1.7以前:

JDK 1.7以前驼侠,intern方法會把對象拷貝到常量池∽晃猓看下面例子:

例一:

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

圖解上述代碼:


image.png

首先new String("str")會在堆中創(chuàng)建str倒源,同時添加到常量池;new String("01")也是一樣的句狼,在堆中創(chuàng)建01笋熬,同時添加到常量池;然后兩者拼接腻菇,底層用的append方法胳螟,在堆中生成一個str01昔馋;然后str1.intern(),就把str01拷貝到常量池了糖耸;此時運行到String str2 = "str01"秘遏,發(fā)現(xiàn)常量池中有了,所以直接指向常量池中的str01嘉竟。最終str1指向堆中的str01對象邦危,str2指向常量池的str01對象,所以結(jié)果是false舍扰。

例二:

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

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


image.png

換一下順序,區(qū)別就在于執(zhí)行到第二行代碼的時候妥粟,常量池中就已經(jīng)有str01了,所以再執(zhí)行str1.intern()的時候吏够,就沒有再進行拷貝了勾给。最終還是str1指向堆中的str01,str2指向常量池的str01锅知,所以結(jié)果還是false播急。

2、JDK1.7以后(包括1.7):

從JDK 1.7開始售睹,intern方法做了些改變桩警,進行拷貝的時候不是拷貝對象,而是拷貝地址值昌妹〈肥啵看下面的例子:

例一:

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

圖解上述代碼:

image.png

第一步和JDK 1.7之前是一樣的,現(xiàn)在堆中創(chuàng)建一個str飞崖,同時搞到常量池烂叔,再創(chuàng)建一個01,同時搞到常量池固歪,然后拼接蒜鸡,在堆中生成對象str01;不同的就是str1.intern()牢裳,這次拷貝的不是str01這個對象逢防,而是把它的地址值搞到常量池中去了;然后執(zhí)行String str2 = str01的時候蒲讯,去常量池找str01忘朝,發(fā)現(xiàn)常量池中有x001地址值,剛好該地址值對應的就是要找的str01判帮,就直接拿過來用辜伟。最終就是str1指向地址值為x001的對象氓侧,str2也是指向地址值為x001的對象,所以結(jié)果是true导狡。

例二:

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

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


image.png

第一步就不多說了,執(zhí)行第二步時旱捧,往常量池中找str01独郎,發(fā)現(xiàn)沒有,那就添加一個枚赡;再執(zhí)行str1.intern()時氓癌,發(fā)現(xiàn)常量池中有str01了,就不進行地址值的拷貝了贫橙。最終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()疲迂,看看會有什么變化:

image.png

本來str1是指向堆中的str01的,然后重新將str1.intern()賦給str1莫湘,str1.intern()是指向常量池的尤蒿,賦給str1后,所以此時str1也是指向常量池幅垮。所以結(jié)果就是true腰池。

四、String忙芒、StringBuilder和StringBuffer:

String和后兩者的區(qū)別就是String是不可變的示弓,后兩者可變。StringBuilder是JDK 1.5以后提供的呵萨,以前用StringBuffer避乏。StringBuffer和StringBuilder的功能基本一樣,只是StringBuffer是線程安全的甘桑,而StringBuilder不是線程安全的拍皮。
因此,StringBuilder的效率會更高跑杭。

上面字符串拼接部分的案例都是用加號拼接的铆帽,然后也提到了StringBuilder的append方法。其實就算是加號拼接德谅,底層還是用的StringBuilder的append方法爹橱。看下面代碼:

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

這就用加號拼接的例子窄做,利用反編譯工具看看這段代碼到底編譯成了啥:

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

可看到愧驱,編譯后是用StringBuilder的append方法進行拼接的爵憎。那么使用加號和使用append方法到底有什么區(qū)別呢慢宗?看一下以下代碼:

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

這個例子很簡單,就是在循環(huá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對象某抓,然后用其append方法進行拼接员魏。這里是i從0到9猪贪,也就是說要new十次,會創(chuàng)建十個對象盆偿,這樣就會占用大量的資源柒爸。所以要讓其編譯后創(chuàng)建StringBuilder對象的過程在循環(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對象了事扭。

小結(jié):當要在循環(huán)里面進行字符串拼接的時候捎稚,就該先在循環(huán)外面new一個StringBuilder,然后在循環(huán)里面用append進行拼接求橄;其他情況就可以使用加號進行拼接更加簡單今野。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市谈撒,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌匾南,老刑警劉巖啃匿,帶你破解...
    沈念sama閱讀 211,376評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異蛆楞,居然都是意外死亡溯乒,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評論 2 385
  • 文/潘曉璐 我一進店門豹爹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來裆悄,“玉大人,你說我怎么就攤上這事臂聋」饧冢” “怎么了?”我有些...
    開封第一講書人閱讀 156,966評論 0 347
  • 文/不壞的土叔 我叫張陵孩等,是天一觀的道長艾君。 經(jīng)常有香客問我,道長肄方,這世上最難降的妖魔是什么冰垄? 我笑而不...
    開封第一講書人閱讀 56,432評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮权她,結(jié)果婚禮上虹茶,老公的妹妹穿的比我還像新娘逝薪。我一直安慰自己,他們只是感情好蝴罪,可當我...
    茶點故事閱讀 65,519評論 6 385
  • 文/花漫 我一把揭開白布董济。 她就那樣靜靜地躺著,像睡著了一般洲炊。 火紅的嫁衣襯著肌膚如雪感局。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,792評論 1 290
  • 那天暂衡,我揣著相機與錄音询微,去河邊找鬼。 笑死狂巢,一個胖子當著我的面吹牛撑毛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播唧领,決...
    沈念sama閱讀 38,933評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼藻雌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了斩个?” 一聲冷哼從身側(cè)響起胯杭,我...
    開封第一講書人閱讀 37,701評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎受啥,沒想到半個月后做个,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡滚局,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,488評論 2 327
  • 正文 我和宋清朗相戀三年居暖,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片藤肢。...
    茶點故事閱讀 38,626評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡太闺,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出嘁圈,到底是詐尸還是另有隱情省骂,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評論 4 329
  • 正文 年R本政府宣布最住,位于F島的核電站冀宴,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏温学。R本人自食惡果不足惜略贮,卻給世界環(huán)境...
    茶點故事閱讀 39,896評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧逃延,春花似錦览妖、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至拄丰,卻和暖如春府树,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背料按。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工奄侠, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人载矿。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓垄潮,卻偏偏與公主長得像,于是被迫代替她去往敵國和親闷盔。 傳聞我的和親對象是個殘疾皇子弯洗,可洞房花燭夜當晚...
    茶點故事閱讀 43,494評論 2 348

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

  • 從網(wǎng)上復制的,看別人的比較全面逢勾,自己搬過來牡整,方便以后查找。原鏈接:https://www.cnblogs.com/...
    lxtyp閱讀 1,345評論 0 9
  • String 的聲明 由 JDK 中關(guān)于String的聲明可以知道: 不同字符串可能共享同一個底層char數(shù)組溺拱,例...
    CodeKing2017閱讀 1,619評論 1 2
  • 前言 RTFSC (Read the fucking source code )才是生活中最重要的逃贝。我們天天就是要...
    二毛_coder閱讀 451評論 1 1
  • 前言: Java中有一個String類攒菠,特別讓人傷腦筋。因為它可以直接賦值歉闰,也可以new一下用構(gòu)造器生成對象辖众,還可...
    貪挽懶月閱讀 3,115評論 5 34
  • ??需要說明的一點是,這篇文章是以《深入理解Java虛擬機》第二版這本書為基礎的和敬,這里假設大家已經(jīng)了解了JVM的運...
    Geeks_Liu閱讀 14,003評論 5 44