String,是Java中最重要的類。這句確定的推斷不是Java之父詹姆斯·高斯林說的,而是沉默王二說的,所以你沒必要懷疑它的準確性。html
關於字符串,有不少的面試題,但我總以爲理論知識繞來繞去沒多大意思。你好比說:String cmower = new String("沉默王二");
定義了幾個對象?java
我總以爲問我這樣的問題,就好像是在拷問我:「既然你家買了冰箱,你難道不該該知道冰箱製冷的原理?」面試
再說,爲何要用String cmower = new String("沉默王二");
而不是String cmower = "沉默王二";
?apache
我勸各位面試官不要再纏住這樣的問題不放了,切記「學以至用」。理論知識若是一直是在繞彎彎,那真的毫無價值。若是要我來作面試官,我想要問的問題是:「你日常是怎麼判斷兩個字符串相等的?是用equals()仍是==?」編程
前言就說這麼多。接下來,咱們來探討幾個實用的知識點。數組
咱們來看一下String類的定義:緩存
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { }
能夠發現,String類是final類型的,所以不能被繼承。安全
若是類能夠被繼承,那麼就會破壞類的不可變性機制。由於子類能夠覆蓋父類的方法,而且能夠改變父類的成員變量值,一旦子類以父類的形式出現時,就不能保證類是不可變的。性能優化
String類的不可變性有什麼好處呢?微信
1)做爲HashMap的鍵。
由於字符串是不可變的,所以它在建立的時候哈希碼(hash code)就計算好了。這也就意味着每次在使用一個字符串的哈希碼的時候不用從新計算一次,這樣更加高效,很適合做爲HashMap中的鍵。
2)線程安全。
同一個字符串對象能夠被多個線程共享,若是訪問頻繁的話,能夠省略同步和鎖等待的時間,從而提高性能。
3)字符串常量池的須要。
特別要注意的是,String類的全部方法都沒有改變字符串自己的值,都是返回了一個新的對象。
在Java中,經常使用的建立字符串的方式有兩種:
String cmower = "沉默王二"; String cmowsan = new String("沉默王三");
cmower使用雙引號,cmowsan使用new關鍵字,它們有什麼區別呢?
答案以下:
String cmower = "沉默王二"; String cmower1 = "沉默王二"; System.out.println(cmower == cmower1); // 輸出true String cmowsan = new String("沉默王三"); String cmowsan1 = new String("沉默王三"); System.out.println(cmowsan == cmowsan1); // 輸出false
雙引號建立的相同字符串使用==
判斷時結果爲true,而new關鍵字建立的相同字符串使用==
判斷時結果爲false。
這是爲何呢?
String在Java中使用過於頻繁,爲了不在系統中產生大量的String對象,Java的設計者引入了「字符串常量池」的概念。
當使用雙引號建立一個字符串時,首先會檢查字符串常量池中是否有相同的字符串對象,若是有,則直接從常量池中取出對象引用;若是沒有,則新建字符串對象,並將其放入字符串常量池中,並返回對象引用。
這也就是說,"沉默王二"是放在字符串常量池中的,cmower和cmower1兩個字符串對象引用是相同的。
而new關鍵字建立的字符串對象是不涉及字符串常量池的,直接放在堆中,也就是說,雖然cmowsan和cmowsan1都叫沉默王三,但不一我的。
強烈建議:不要使用new關鍵字的形式建立字符串對象。
因爲字符串是不可變的,所以字符串在進行拼接的時候會建立新的字符串對象。你們都知道,內存是必定的,所以對象建立多了就會影響系統性能。
StringBuilder正是爲了解決字符串拼接產生太多中間對象的問題而提供的一個類,能夠經過append()方法把字符串添加到已有序列的末尾,很是高效。
那麼有人在進行字符串拼接的時候,就會產生疑惑:「我究竟是用+號仍是StringBuilder?」
咱們先來看這樣一段代碼:
String chenmo = "沉默"; String wanger = "王二"; System.out.println(chenmo + wanger);
這段代碼是怎麼編譯的呢?可使用JAD(Java反編譯工具)來看一看。
String s = "\u5A0C\u5910\u7CAF"; String s1 = "\u941C\u5B29\u7C29"; System.out.println((new StringBuilder()).append(s).append(s1).toString());
你是否是看到了StringBuilder
的影子?
沒錯,使用+號進行字符串拼接的時候,Java編譯器實際是經過StringBuilder類來完成的。
難道可使用+號來隨意拼接字符串?反正Java編譯器已經自動地爲咱們優化了。
但事實並不是如此,來看這樣一段代碼:
String cmowers = ""; for (int i = 0; i < 9; i++) { cmowers += "沉默王二"; } System.out.println(cmowers);
閉上眼睛先想想,Java編譯器會怎麼作?咱們指望的結果是在循環外部就建立StringBuilder,Java編譯器能如咱們所願嗎?
JAD反編譯後的結果以下:
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);
這麼看來,StringBuilder是在for循環內部建立的,也就是說會建立10次。天吶,這可不是咱們指望的結果!咱們只但願StringBuilder建立一次。
沒辦法,Java編譯器是作不到的,只能靠咱們本身:
StringBuilder cmowers = new StringBuilder(); for (int i = 0; i < 9; i++) { cmowers.append("沉默王二"); } System.out.println(cmowers);
強烈建議:若是隻是三四個字符串的拼接,儘管使用+號操做符,別想什麼性能優化(舉個例子,你離目的地只有100米,你是打算打個出租車,仍是本身步行走過去?);若是遇到多於四個字符串的拼接,或者須要用到循環來拼接,那就選擇StringBuilder。
在我年輕的時候,我還會犯這樣一個錯誤:
StringBuilder cmowers = new StringBuilder(); for (int i = 0; i < 9; i++) { cmowers.append("沉默王二" + "和他的讀者朋友們"); } System.out.println(cmowers);
我去,居然在append()方法的內部使用+號!由於這個錯誤,我差點沒被領導打死。你可要當心點。
除了使用+號和StringBuilder對字符串進行拼接,還可使用String類的concat()
方法。
concat()方法只不過是String類的一個方法而已,爲何我要單獨拎出來講呢?
由於以前我要在JSP頁面的EL表達式中拼接字符串,剛開始想到的是用+號操做符,但EL表達式不是Java,+號操做符是不能拼接字符串的。我當時居然沒想起來用concat()
!
從新銘記一下:
${item.username.concat('-').concat(item.realname)}
關於字符串的性能問題,我常在一些技術文章中看到這樣的建議:「若是一個字符串使用的頻率很是高,建議使用String.intern()
將其緩存。」
但我並不建議你這麼作,由於這個方法要顯式的調用,這樣很麻煩;何況,在代碼編寫階段,怎麼可能知道哪一個字符串使用頻率很高呢?
據個人編程經驗來看,字符串的操做每每須要用到一個工具類,那就是org.apache.commons.lang3.StringUtils
(null安全的,也就是說,StringUtils類的方法能夠接受爲null的字符串,但不會拋出NullPointerException)。
不過,我最經常使用的方法就那麼幾個:
方法 | 等價 |
---|---|
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) |
把數組中的元素鏈接成一個字符串返回 |
上一篇:Java內部類
下一篇:Java 數組,看這篇就夠了
微信搜索「沉默王二」公衆號,關注後回覆「免費視頻」獲取 500G Java 高質量教學視頻(已分門別類)。