深入理解java中的String

今天咱們一起來說說java家族中的使用頻率最廣泛的String

  • String的特性
    在Sring源碼的類注釋當中有如下一段話:


    這說明了String的一個重要特性,String 是value不可改變的
    然而String類還使用了final來修飾,表明String的第二個重要特性,** String是不可被繼承的**

  • String的定義方法
    一般我們在代碼中可以使用如下三種方法定義String對象

String str1 = new String("cat");
String str2 = "cat";
String str3 = "c"+"at";
String str4 = str1+"cat";

第一種方式直接通過關鍵字new創建對象,毫無疑問str1指向堆內存
第二種直接賦值,Str2指向常量池
第三種通過+號連接賦,最終str3也是指向常量池
第四種是帶三種的延伸,這個最終str4是指向堆內存
對于前三種方式大家應該都沒什么疑問,那么第四種方法為什么也會指向堆內存呢?欲知真相還需從字節碼入手啊!

先來看看幾個經典的面試題
  • First
String s1 = "abc";
String s2 = "abc";
System.out.println("s1 == s2 : " + (s1 == s2)); //true

So esay,對吧!

  • Second
String s1 = "abc";
String s2 = new String("abc");
String s3 = new String("abc");
System.out.println("s2 == s3 : " + (s2 == s3)); //false
System.out.println("s1 == s3 : " + (s1 == s3));//false

還是很簡單,對么,只要有點基礎的人都知道答案呢!

  • Third
 public class Test{
    public static void main(String[] args){
        
        String str1 = "ab" + "cd"; // 
        String str11 = "abcd";
        //System.out.println("str1 == str11 : " + (str1 == str11)); //true
        
        String str2 = "ab"+2;
        String str22 = "ab2";
        //System.out.println("str2 == str22 : " + (str2 == str22)); //true

        final String str3 = "ab"; 
        String str31 = str3+"cd"; 
        String str32 = "abcd";
        //System.out.println("str2 = str3 : " + (str2 == str3)); // false
    }
}

    

這個對新手來說可能就有點不知所措了,來來來,上我們的終極利器,請看下面編譯之后的字節碼


因為常量的值在編譯期間就已經確定了,這里"ab","cd","2"都是常量,所以編譯器會自動對String str1 = "ab" + "cd";String str2 = "ab"+2;進行優化,效果相當于String str1 = "abcd";String str2 = "ab2"; 這下你明白為什么結果會是true了吧

  • Fourth
public class Test{
    public static void main(String[] args){     
        String str2 = "ab"; 
        String str3 = "cd"; 
        String str41 = str2 + str3;
        String str42 = "ab"+str3;
        String str43 = str2+"cd";
        String str5 = "abcd";
        //System.out.println("str41 = str5 : " + (str41 == str5)); // false
        //System.out.println("str42 = str5 : " + (str42 == str5)); // false
        //System.out.println("str43 = str5 : " + (str43 == str5)); // false
    }
}

這個跟例三差不多,只不過例三是常量,這里變成了變量,結果就迥然不同,下面我們來看看這段代碼的字節碼長啥樣子



發現虛擬機在處理變量字符串用+號連接的時候是生成了一個StringBuilder對象,然后調用StringBuilder的append方法,最后在調用StringBuilder的toString方法返回。StringBuilder的toString方法源碼如下:



這下就徹底明白了吧。它是從新new一個String對象,結果當然為false了!
針對==的總結如下

眾所周知,==是判斷內存地址的,那么我們判斷的話看引用最終指向哪里不就可以了么

  1. new出來的對象是存放在堆內存中的,引用指向堆內存,那么地址肯定是唯一的了,所以結果肯定為false
  2. 直接聲明的字符串引用是指向常量池的,而常量池中的相同內容的字符串只有一份,所以結果為true
  3. 通過+號連接的字符串分如下兩種情況
    3.1 如果+號兩邊連接的是常量,那么會在編譯期間進行優化,結果同2
    3.2 如果+號兩邊連接的有變量,不管是new出來的也好,直接聲明的也好,java虛擬機執行的時候都會生成一個StringBuilder對象sb,然后調用sb.apend()方法,最后通過sb.toString()返回一個新的字符串。那么此時引用就指向堆內存,結果同1

String.intern()

當一個String實例str調用intern()方法時,虛擬機會查找常量池中是否有相同Unicode的字符串常量,如果有,則返回其的引用;如果沒有,則在常量池中增加一個Unicode等于str的字符串并返回它的引用;PS:我并沒有想到什么時候會沒有

public class Test{
    public static void main(String[] args){
        String s00 = "kvill"; 
        String s11 = new String("kvill"); 
        String s22 = new String("kvill"); 
        System.out.println( s00 == s11 ); //false
      
        s11.intern(); //雖然執行了s1.intern(),但它的返回值沒有賦給s1
        s22 = s22.intern(); //把常量池中"kvill"的引用賦給s2 
        System.out.println( s00 == s11); //flase
        System.out.println( s00 == s11.intern() ); //true 說明s11.intern()返回的是常量池中"kvill"的引用
        System.out.println( s00 == s22 ); //true
    }
}

關于String,StringBuffer,StringBuilder的區別
  1. String是值不可變的,每次進行連接操作是都是返回一個新的String對象,StringBuffer,StringBuilder是值可變的,操作是返回的是this
    這也就是為什么在進行大量字符串連接運算時,不推薦使用String,而推薦StringBuffer和StringBuilder。
  2. StringBuffer是線程同步的,安全性高,但執行效率低
    StringBuilder是非線程同步的,安全性低,但執行效率高

這是StringBuffer,StringBuilder append方法的源碼




THE END

如有錯誤之處還請大家不吝賜教,多多指正,共同進步!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,825評論 6 546
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,814評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,980評論 0 384
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 64,064評論 1 319
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,779評論 6 414
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,109評論 1 330
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,099評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,287評論 0 291
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,799評論 1 338
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,515評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,750評論 1 375
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,221評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,933評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,327評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,667評論 1 296
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,492評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,703評論 2 380

推薦閱讀更多精彩內容

  • java筆記第一天 == 和 equals ==比較的比較的是兩個變量的值是否相等,對于引用型變量表示的是兩個變量...
    jmychou閱讀 1,516評論 0 3
  • 一.你了解String類嗎? 想要了解一個類,最好的辦法就是看這個類的實現源代碼,String類的實現在 \jdk...
    Viking_Den閱讀 675評論 0 3
  • 『叮! 深夜我突然想起一年多前的一件事來,但是我不知道該怎么去描述。 就擬人吧。 從前,我很喜歡很喜歡一個男生,但...
    綿花不白閱讀 265評論 2 0
  • 拍攝設備 尼康700D 拍攝地點時間 信陽師范學院 晚8:30
    陳凱字東洲閱讀 205評論 0 3
  • 曾經是個努力,純真的小女孩 努力做功課,喜歡看書,參加許多活動,獲得許多獎,成績優秀,只為成為自己想要成為的人 現...
    薄荷味彩鉛閱讀 192評論 0 0