java源碼學(xué)習(xí)-String

String 源碼學(xué)習(xí)

1. String

1.1不變性

不可變指的是類值一旦被初始化腕柜,就不能被修改,如果被修改那將會是新的類。

        String s  = "hello";
        s = "world";

從代碼上看前弯,s的值好像被修改了但是debug來看


看地址為820

地址變?yōu)?22

s的內(nèi)存地址已經(jīng)被修改了,也就是說 s= "world",看似簡單的賦值秫逝,其實已經(jīng)將s的引用指向了新的String恕出。
補充(使用idea dubug模式,@XXX;就是變量的相對內(nèi)存地址)

查看源碼


string源碼

通過源碼可以看出

  1. string類被final修飾违帆,說明string類不能夠被繼承浙巫,也就是說對任何string的操作方法,都不會被繼承覆蓋。
  2. string保存數(shù)據(jù)是byte[]的畴。我們看到value也是被final修飾的渊抄,也就是說value一旦被賦值,那內(nèi)存地址就無法被修改丧裁,而且value的權(quán)限是private的护桦,外部訪問不到,string也沒有開放出對value進行賦值的方法煎娇,所以說value一旦產(chǎn)生二庵,內(nèi)存地址根本無法被修改。
    注:(在jdk8中string實現(xiàn)是char[] ,而在jdk9中變更成byte[] ,使用byte數(shù)組可以減少一半的內(nèi)存缓呛,byte使用一個字節(jié)來存儲一個char字符催享,char使用兩個字節(jié)來存儲一個char字符。只有當(dāng)一個char字符大小超過0xFF時哟绊,才會將byte數(shù)組變?yōu)樵瓉淼膬杀兑蛎睿脙蓚€字節(jié)存儲一個char字符。)

以上兩點是string不變性原因票髓,充分利用了final關(guān)鍵字的特性兰迫,如果你定義類時也希望是不變的,可以參考string這兩點操作炬称。

因為string的不變性汁果,所以string的大多數(shù)操作方法,都會返回新的string玲躯,下面需要注意

        String str ="hello world";
        // 這種寫法是替換不掉的据德,必須接受 replace 方法返回的參數(shù)才行,這樣才行:str = str.replace("h","xx");
        str.replace("h","xx");
        System.out.println(str);
        str = str.replace("d", "qq");
        System.out.println(str);

1.2 字符串亂碼

在平時進行二進制轉(zhuǎn)化操作時跷车,本地測試沒問題棘利,但是運行到其他機器上時,會出現(xiàn)字符串亂碼情況朽缴,主要原因是在進行二進制轉(zhuǎn)化操作時善玫,并沒有強制規(guī)定文件編碼,而不同的環(huán)境默認(rèn)的文件編碼不一致導(dǎo)致密强。

模擬字符串亂碼

輸出 : nihao ?? ??

打印的結(jié)果為茅郎??或渤,這就是常見的亂碼表現(xiàn)形式系冗。這時候有人說,是不是我把代碼修改成 String s2 = new String(bytes,"ISO-8859-1"); 就可以了薪鹦?這是不行的掌敬。主要是因為 ISO-8859-1 這種編碼對中文的支持有限惯豆,導(dǎo)致中文會顯示亂碼。唯一的解決辦法奔害,就是在所有需要用到編碼的地方楷兽,都統(tǒng)一使用 UTF-8,對于 String 來說华临,getBytes 和 new String 兩個方法都會使用到編碼拄养,我們把這兩處的編碼替換成 UTF-8 后,打印出的結(jié)果就正常了银舱。

1.3首字母大小寫

如果項目被spring托管,有時候我們會通過 application.getBean(className); 這種方式得到SpringBean跛梗,這時className必須滿足首字母小寫寻馏。除了該場景,在反射場景下核偿,我們也經(jīng)常要使類屬性的首字母小寫诚欠。

str.substring(0,1).toLowerCase()+ str.substring(1);

使用 substring 方法,該方法主要是為了截取字符串連續(xù)的一部分漾岳,substring 有兩個方法:

/*beginIndex :開始位置轰绵,結(jié)束位置為文本末尾 */ 
public java.lang.String substring(int beginIndex)  

/*beginIndex :開始位置,endIndex 結(jié)束位置   */ 
public java.lang.String substring(int beginIndex, int endIndex) 

substring 方法的底層使用的是字符數(shù)組范圍截取的方法 :

Arrays.copyOfRange(字符數(shù)組, 開始位置, 結(jié)束位置); 

從字符數(shù)組中進行一段范圍的拷貝尼荆。

1.4相等判斷

我們判斷相等有兩種辦法左腔,equals 和 equalsIgnoreCase。后者判斷相等時捅儒,會忽略大小寫液样。

一些面試題在問:如果讓你寫判斷兩個 String 相等的邏輯,應(yīng)該如何寫巧还,我們來一起看下 equals 的源碼(之前的源碼)鞭莽,整理一下思路:

public boolean equals(Object anObject) {
    // 判斷內(nèi)存地址是否相同
    if (this == anObject) {
        return true;
    }
    // 待比較的對象是否是 String,如果不是 String麸祷,直接返回不相等
    if (anObject instanceof String) {
        String anotherString = (String)anObject;
        int n = value.length;
        // 兩個字符串的長度是否相等澎怒,不等則直接返回不相等
        if (n == anotherString.value.length) {
            char v1[] = value;
            char v2[] = anotherString.value;
            int i = 0;
            // 依次比較每個字符是否相等,若有一個不等阶牍,直接返回不相等
            while (n-- != 0) {
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            return true;
        }
    }
    return false;
}

目前源碼
String重寫Object的equals方法喷面,先用“==”判斷地址,地址相同則直接返回true走孽;然后再比較類型乖酬,類型不同則直接返回false;最后才比較內(nèi)容融求。
用于比較兩字符串對象是否相等咬像,如果引用相同則返回 true。否則判斷比較對象是否為 String 類的實例,是的話轉(zhuǎn)成 String 類型县昂,接著比較編碼是否相同肮柜,分別以 LATIN1 編碼和 UTF16 編碼進行比較。
代碼如下:

public boolean equals(Object anObject) {
        //判斷地址是否相等
        if (this == anObject) {
            return true;
        } else {
            //比較類型
            if (anObject instanceof String) {
                //比較內(nèi)容
                //轉(zhuǎn)成 String 類型倒彰,接著比較編碼是否相同审洞,
                //分別以 LATIN1 編 碼和 UTF16 編碼進行比較
                String aString = (String)anObject;
                if (this.coder() == aString.coder()) {
                    return this.isLatin1() ? StringLatin1.equals(this.value, aString.value) : StringUTF16.equals(this.value, aString.value);
                }
            }

            return false;
        }
    }

由于equals是Objec的方法,意味著任意引用類型對象都可以調(diào)用待讳,而且芒澜,入?yún)⑹荗bject類型,所以创淡,不同類型是可以用equals()方法的痴晦,不會像“==”一樣編譯異常。
注:會遇到的一個小坑琳彩,例如:char chr = ‘a(chǎn)’誊酌,String str = “a”,我經(jīng)常會寫成str.equals(chr)露乏,而且還傻傻的等著返回true碧浊,上面說到過,兩個不同類型的變量比較瘟仿,equals()會直接返回false箱锐。str.equals(chr+"")倒是可以解決。

1.5替換劳较、刪除

有 replace 替換所有字符瑞躺、replaceAll 批量替換字符串、replaceFirst 替換遇到的第一個字符串三種場景兴想。


替換

replace 有兩個方法幢哨,一個入?yún)⑹?char,一個入?yún)⑹?String嫂便,
前者表示替換所有字符捞镰,如:str.replace('a','b')
后者表示替換所有字符串,如:str.replace("a","b")兩者就是單引號和多引號的區(qū)別毙替。

需要注意的是岸售, replace 并不只是替換一個,是替換所有匹配到的字符或字符串哦厂画。

如果是刪除 某些字符凸丸,也可以使用 replace 方法,把想刪除的字符替換成 “” 即可

1.6拆分袱院,合并

拆分我們使用 split 方法屎慢,該方法有兩個入?yún)?shù)瞭稼。第一個參數(shù)是我們拆分的標(biāo)準(zhǔn)字符,第二個參數(shù)是一個 int 值腻惠,叫 limit环肘,來限制我們需要拆分成幾個元素。如果 limit 比實際能拆分的個數(shù)小集灌,按照 limit 的個數(shù)進行拆分悔雹。


分割

測試數(shù)據(jù)
輸出

從演示的結(jié)果來看,limit 對拆分的結(jié)果欣喧,是具有限制作用的腌零,還有就是拆分結(jié)果里面不會出現(xiàn)被拆分的字段。

那如果字符串里面有一些空值呢唆阿,空值是拆分不掉的益涧,仍然成為結(jié)果數(shù)組的一員,如果我們想刪除空值酷鸦,只能自己拿到結(jié)果后再做操作。
但 Guava(Google 開源的技術(shù)工具) 提供了一些可靠的工具類牙咏,可以幫助我們快速去掉空值臼隔,如下:

List<String> on = Splitter.on(":")
                .trimResults() //去掉空值
                .omitEmptyStrings() //去掉空格
                .splitToList(s);

輸出:

[bbb, aaa, oo, o, asd]

合并我們使用 join 方法,此方法是靜態(tài)的妄壶,我們可以直接使用摔握。方法有兩個入?yún)ⅲ瑓?shù)一是合并的分隔符丁寄,參數(shù)二是合并的數(shù)據(jù)源氨淌,數(shù)據(jù)源支持?jǐn)?shù)組和 List,在使用的時候伊磺,我們發(fā)現(xiàn)有兩個不太方便的地方:

  1. 不支持依次 join 多個字符串盛正,比如我們想依次 join 字符串 s 和 s1,如果你這么寫的話 String.join(",",s).join(",",s1) 最后得到的是 s1 的值屑埋,第一次 join 的值被第二次 join 覆蓋了豪筝;
  2. 如果 join 的是一個 List,無法自動過濾掉 null 值摘能。
// 依次 join 多個字符串续崖,Joiner 是 Guava 提供的 API
Joiner joiner = Joiner.on(",").skipNulls();
String result = joiner.join("hello",null,"china");

List<String> list = Lists.newArrayList(new String[]{"hello","china",null});
// 輸出的結(jié)果為;
依次 join 多個字符串:hello,china
自動刪除 list 中空值:hello,china
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末团搞,一起剝皮案震驚了整個濱河市严望,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌逻恐,老刑警劉巖像吻,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件峻黍,死亡現(xiàn)場離奇詭異,居然都是意外死亡萧豆,警方通過查閱死者的電腦和手機奸披,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來涮雷,“玉大人阵面,你說我怎么就攤上這事『檠迹” “怎么了样刷?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長览爵。 經(jīng)常有香客問我置鼻,道長,這世上最難降的妖魔是什么蜓竹? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任箕母,我火速辦了婚禮,結(jié)果婚禮上俱济,老公的妹妹穿的比我還像新娘嘶是。我一直安慰自己,他們只是感情好蛛碌,可當(dāng)我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布聂喇。 她就那樣靜靜地躺著,像睡著了一般蔚携。 火紅的嫁衣襯著肌膚如雪希太。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天酝蜒,我揣著相機與錄音誊辉,去河邊找鬼。 笑死亡脑,一個胖子當(dāng)著我的面吹牛芥映,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播远豺,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼奈偏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了躯护?” 一聲冷哼從身側(cè)響起惊来,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎棺滞,沒想到半個月后裁蚁,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體矢渊,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年枉证,在試婚紗的時候發(fā)現(xiàn)自己被綠了矮男。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡室谚,死狀恐怖毡鉴,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情秒赤,我是刑警寧澤猪瞬,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站入篮,受9級特大地震影響陈瘦,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜潮售,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一痊项、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧酥诽,春花似錦鞍泉、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽漱逸。三九已至泪姨,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間饰抒,已是汗流浹背肮砾。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留袋坑,地道東北人仗处。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像枣宫,于是被迫代替她去往敵國和親婆誓。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,941評論 2 355

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