溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Java String,看這篇就夠了

發(fā)布時(shí)間:2020-07-22 23:04:07 來(lái)源:網(wǎng)絡(luò) 閱讀:121 作者:沉默王二 欄目:編程語(yǔ)言

String,是Java中最重要的類。這句肯定的推斷不是Java之父詹姆斯·高斯林說(shuō)的,而是沉默王二說(shuō)的,因此你不必懷疑它的準(zhǔn)確性。

關(guān)于字符串,有很多的面試題,但我總覺(jué)得理論知識(shí)繞來(lái)繞去沒(méi)多大意思。你比如說(shuō):String cmower = new String("沉默王二");定義了幾個(gè)對(duì)象?

我總覺(jué)得問(wèn)我這樣的問(wèn)題,就好像是在拷問(wèn)我:“既然你家買了冰箱,你難道不應(yīng)該知道冰箱制冷的原理?”

再說(shuō),為什么要用String cmower = new String("沉默王二");而不是String cmower = "沉默王二";?

我勸各位面試官不要再纏住這樣的問(wèn)題不放了,切記“學(xué)以致用”。理論知識(shí)如果一直是在繞彎彎,那真的毫無(wú)價(jià)值。如果要我來(lái)做面試官,我想要問(wèn)的問(wèn)題是:“你平常是怎么判斷兩個(gè)字符串相等的?是用equals()還是==?”

前言就說(shuō)這么多。接下來(lái),我們來(lái)探討幾個(gè)實(shí)用的知識(shí)點(diǎn)。

01、 字符串是不可變的

我們來(lái)看一下String類的定義:

public?final?class?String
????implements?java.io.Serializable,?Comparable<String>,?CharSequence?
{
}

可以發(fā)現(xiàn),String類是final類型的,因此不能被繼承。

如果類可以被繼承,那么就會(huì)破壞類的不可變性機(jī)制。因?yàn)樽宇惪梢愿采w父類的方法,并且可以改變父類的成員變量值,一旦子類以父類的形式出現(xiàn)時(shí),就不能保證類是不可變的。

String類的不可變性有什么好處呢?

1)作為HashMap的鍵。

因?yàn)樽址遣豢勺兊模虼怂趧?chuàng)建的時(shí)候哈希碼(hash code)就計(jì)算好了。這也就意味著每次在使用一個(gè)字符串的哈希碼的時(shí)候不用重新計(jì)算一次,這樣更加高效,很適合作為HashMap中的鍵。

2)線程安全。

同一個(gè)字符串對(duì)象可以被多個(gè)線程共享,如果訪問(wèn)頻繁的話,可以省略同步和鎖等待的時(shí)間,從而提升性能。

3)字符串常量池的需要。

特別要注意的是,String類的所有方法都沒(méi)有改變字符串本身的值,都是返回了一個(gè)新的對(duì)象

推薦閱讀:為什么 Java 字符串是不可變的?

02、 字符串常量池

在Java中,常用的創(chuàng)建字符串的方式有兩種:

String?cmower?=?"沉默王二";

String?cmowsan?=?new?String("沉默王三");

cmower使用雙引號(hào),cmowsan使用new關(guān)鍵字,它們有什么區(qū)別呢?

答案如下:

String?cmower?=?"沉默王二";
String?cmower1?=?"沉默王二";
System.out.println(cmower?==?cmower1);?//?輸出true

String?cmowsan?=?new?String("沉默王三");
String?cmowsan1?=?new?String("沉默王三");
System.out.println(cmowsan?==?cmowsan1);?//?輸出false

雙引號(hào)創(chuàng)建的相同字符串使用==判斷時(shí)結(jié)果為true,而new關(guān)鍵字創(chuàng)建的相同字符串使用==判斷時(shí)結(jié)果為false。

這是為什么呢?

String在Java中使用過(guò)于頻繁,為了避免在系統(tǒng)中產(chǎn)生大量的String對(duì)象,Java的設(shè)計(jì)者引入了“字符串常量池”的概念

當(dāng)使用雙引號(hào)創(chuàng)建一個(gè)字符串時(shí),首先會(huì)檢查字符串常量池中是否有相同的字符串對(duì)象,如果有,則直接從常量池中取出對(duì)象引用;如果沒(méi)有,則新建字符串對(duì)象,并將其放入字符串常量池中,并返回對(duì)象引用。

這也就是說(shuō),"沉默王二"是放在字符串常量池中的,cmower和cmower1兩個(gè)字符串對(duì)象引用是相同的。

而new關(guān)鍵字創(chuàng)建的字符串對(duì)象是不涉及字符串常量池的,直接放在堆中,也就是說(shuō),雖然cmowsan和cmowsan1都叫沉默王三,但不一個(gè)人。

強(qiáng)烈建議:不要使用new關(guān)鍵字的形式創(chuàng)建字符串對(duì)象。

03、 +號(hào)和StringBuilder

由于字符串是不可變的,因此字符串在進(jìn)行拼接的時(shí)候會(huì)創(chuàng)建新的字符串對(duì)象。大家都知道,內(nèi)存是一定的,因此對(duì)象創(chuàng)建多了就會(huì)影響系統(tǒng)性能。

StringBuilder正是為了解決字符串拼接產(chǎn)生太多中間對(duì)象的問(wèn)題而提供的一個(gè)類,可以通過(guò)append()方法把字符串添加到已有序列的末尾,非常高效。

那么有人在進(jìn)行字符串拼接的時(shí)候,就會(huì)產(chǎn)生疑惑:“我到底是用+號(hào)還是StringBuilder?”

我們先來(lái)看這樣一段代碼:

String?chenmo?=?"沉默";
String?wanger?=?"王二";
System.out.println(chenmo?+?wanger);

這段代碼是怎么編譯的呢?可以使用JAD(Java反編譯工具)來(lái)看一看。

String?s?=?"\u5A0C\u5910\u7CAF";
String?s1?=?"\u941C\u5B29\u7C29";
System.out.println((new?StringBuilder()).append(s).append(s1).toString());

你是不是看到了StringBuilder的影子?

沒(méi)錯(cuò),使用+號(hào)進(jìn)行字符串拼接的時(shí)候,Java編譯器實(shí)際是通過(guò)StringBuilder類來(lái)完成的。

難道可以使用+號(hào)來(lái)隨意拼接字符串?反正Java編譯器已經(jīng)自動(dòng)地為我們優(yōu)化了。

但事實(shí)并非如此,來(lái)看這樣一段代碼:

String?cmowers?=?"";
for?(int?i?=?0;?i?<?9;?i++)?{
????cmowers?+=?"沉默王二";
}
System.out.println(cmowers);

閉上眼睛先想一想,Java編譯器會(huì)怎么做?我們期望的結(jié)果是在循環(huán)外部就創(chuàng)建StringBuilder,Java編譯器能如我們所愿嗎?

JAD反編譯后的結(jié)果如下:

String?s?=?"";
for(int?i?=?0;?i?<?10;?i++)
????s?=?(new?StringBuilder()).append(s).append("\u5A0C\u5910\u7CAF\u941C\u5B29\u7C29").toString();

System.out.println(s);

這么看來(lái),StringBuilder是在for循環(huán)內(nèi)部創(chuàng)建的,也就是說(shuō)會(huì)創(chuàng)建10次。天吶,這可不是我們期望的結(jié)果!我們只希望StringBuilder創(chuàng)建一次。

沒(méi)辦法,Java編譯器是做不到的,只能靠我們自己:

StringBuilder?cmowers?=?new?StringBuilder();
for?(int?i?=?0;?i?<?9;?i++)?{
????cmowers.append("沉默王二");
}
System.out.println(cmowers);

強(qiáng)烈建議:如果只是三四個(gè)字符串的拼接,盡管使用+號(hào)操作符,別想什么性能優(yōu)化(舉個(gè)例子,你離目的地只有100米,你是打算打個(gè)出租車,還是自己步行走過(guò)去?);如果遇到多于四個(gè)字符串的拼接,或者需要用到循環(huán)來(lái)拼接,那就選擇StringBuilder。

在我年輕的時(shí)候,我還會(huì)犯這樣一個(gè)錯(cuò)誤:

StringBuilder?cmowers?=?new?StringBuilder();
for?(int?i?=?0;?i?<?9;?i++)?{
????cmowers.append("沉默王二"?+?"和他的讀者朋友們");
}
System.out.println(cmowers);

我去,竟然在append()方法的內(nèi)部使用+號(hào)!因?yàn)檫@個(gè)錯(cuò)誤,我差點(diǎn)沒(méi)被領(lǐng)導(dǎo)打死。你可要小心點(diǎn)。

04、 關(guān)于concat()

除了使用+號(hào)和StringBuilder對(duì)字符串進(jìn)行拼接,還可以使用String類的concat()方法。

concat()方法只不過(guò)是String類的一個(gè)方法而已,為什么我要單獨(dú)拎出來(lái)說(shuō)呢?

因?yàn)橹拔乙贘SP頁(yè)面的EL表達(dá)式中拼接字符串,剛開始想到的是用+號(hào)操作符,但EL表達(dá)式不是Java,+號(hào)操作符是不能拼接字符串的。我當(dāng)時(shí)竟然沒(méi)想起來(lái)用concat()

重新銘記一下:

${item.username.concat('-').concat(item.realname)}

05、 關(guān)于intern()

關(guān)于字符串的性能問(wèn)題,我常在一些技術(shù)文章中看到這樣的建議:“如果一個(gè)字符串使用的頻率非常高,建議使用String.intern()將其緩存?!?/p>

但我并不建議你這么做,因?yàn)檫@個(gè)方法要顯式的調(diào)用,這樣很麻煩;況且,在代碼編寫階段,怎么可能知道哪個(gè)字符串使用頻率很高呢?

06、 關(guān)于StringUtils

據(jù)我的編程經(jīng)驗(yàn)來(lái)看,字符串的操作往往需要用到一個(gè)工具類,那就是org.apache.commons.lang3.StringUtils(null安全的,也就是說(shuō),StringUtils類的方法可以接受為null的字符串,但不會(huì)拋出NullPointerException)。

不過(guò),我最常用的方法就那么幾個(gè):

方法等價(jià)
IsEmpty(String str)str == null or str.length == 0
isBlank(String str)str == null or str.length == 0 or str.trim().length == 0
join(Object[] arrey)把數(shù)組中的元素連接成一個(gè)字符串返回

上一篇:Java內(nèi)部類

下一篇:Java 數(shù)組,看這篇就夠了

微信搜索「沉默王二」公眾號(hào),關(guān)注后回復(fù)「免費(fèi)視頻」獲取 500G Java 高質(zhì)量教學(xué)視頻(已分門別類)。


向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI