您好,登錄后才能下訂單哦!
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)。
我們來(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 字符串是不可變的?
在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ì)象。
由于字符串是不可變的,因此字符串在進(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)。
除了使用+號(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)}
關(guān)于字符串的性能問(wèn)題,我常在一些技術(shù)文章中看到這樣的建議:“如果一個(gè)字符串使用的頻率非常高,建議使用String.intern()
將其緩存?!?/p>
但我并不建議你這么做,因?yàn)檫@個(gè)方法要顯式的調(diào)用,這樣很麻煩;況且,在代碼編寫階段,怎么可能知道哪個(gè)字符串使用頻率很高呢?
據(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é)視頻(已分門別類)。
免責(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)容。