Java String:重要到別人只能當老二的字符串類

字符串,是Java中最重要的類。這句肯定的推斷不是Java之父詹姆斯·高斯林說的,而是沉默王二說的,因此你不必懷疑它的準確性。

關于字符串,有很多的面試題,但我總覺得理論知識繞來繞去沒多大意思。你比如說:String cmower = new String("沉默王二");定義了幾個對象?

我總覺得問我這樣的問題,就好像是在拷問我:“既然你家買了冰箱,你難道不應該知道冰箱制冷的原理?”

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

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

前言就說這么多。接下來,我們來探討幾個實用的知識點。

01 字符串是不可變的

我們來看一下String類的定義:

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

可以發現,String類是final類型的,因此不能被繼承。

如果類可以被繼承,那么就會破壞類的不可變性機制。因為子類可以覆蓋父類的方法,并且可以改變父類的成員變量值,一旦子類以父類的形式出現時,就不能保證類是不可變的。

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

1)作為HashMap的鍵。

因為字符串是不可變的,因此它在創建的時候哈希碼(hash code)就計算好了。這也就意味著每次在使用一個字符串的哈希碼的時候不用重新計算一次,這樣更加高效,很適合作為HashMap中的鍵。

2)線程安全。

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

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

稍后介紹。

特別要注意的是,String類的所有方法都沒有改變字符串本身的值,都是返回了一個新的對象

02 字符串常量池

在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關鍵字的形式創建字符串對象。

03 +號和StringBuilder

由于字符串是不可變的,因此字符串在進行拼接的時候會創建新的字符串對象。大家都知道,內存是一定的,因此對象創建多了就會影響系統性能。

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()方法的內部使用+號!因為這個錯誤,我差點沒被領導打死。你可要小心點。

04 關于concat()

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

concat()方法只不過是String類的一個方法而已,為什么我要單獨拎出來說呢?

因為之前我要在JSP頁面的EL表達式中拼接字符串,剛開始想到的是用+號操作符,但EL表達式不是Java,+號操作符是不能拼接字符串的。我當時竟然沒想起來用concat()

重新銘記一下:

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

05 關于intern()

關于字符串的性能問題,我常在一些技術文章中看到這樣的建議:“如果一個字符串使用的頻率非常高,建議使用String.intern()將其緩存。”

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

06 關于StringUtils

據我的編程經驗來看,字符串的操作往往需要用到一個工具類,那就是org.apache.commons.lang3.StringUtils(null安全的,也就是說,StringUtils類的方法可以接受為null的字符串,但不會拋出NullPointerException)。

不過,我最常用的方法就那么幾個:

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

?

?

推薦閱讀:

Java異常處理:給程序罩一層保險
Java集合類:我其實沒那么簡單

轉載于:https://www.cnblogs.com/qing-gee/p/10277688.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/249802.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/249802.shtml
英文地址,請注明出處:http://en.pswp.cn/news/249802.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

vue 中slot 的具體用法

子組件 <template><div class"slotcontent"><ul><!--<slot></slot>--><li v-for"item in items">{{item.text}}</li></ul></div> </template><script>export default{data(){re…

Java基礎教程:多線程基礎(3)——阻塞隊列

Java基礎教程&#xff1a;多線程基礎&#xff08;3&#xff09;——阻塞隊列 快速開始 引入問題 生產者消費者問題是線程模型中的經典問題&#xff1a;生產者和消費者在同一時間段內共用同一存儲空間&#xff0c;生產者向空間里生產數據&#xff0c;而消費者取走數據。 模擬情景…

001.Linux開機啟動過程

相關Linux啟動過程解析&#xff0c;此作為通用啟動參考&#xff1a; 轉載于:https://www.cnblogs.com/itzgr/p/10285833.html

學習vim 從常用按鍵開始

撤銷 u 前進 ctrl r移動 下一個單詞 w 當前單詞首或上個單詞首 b 當前單詞尾或上個單詞尾 e ---- 大寫就是忽略標點符號 行首行尾 $^ 查詢 /word 下一個 n 上一個 Nv 可視化操作命令 刪除操作 x 刪除光標處的字符&#xff0c;向后刪除 nx …

element ui 中 el-menu 如何添加鏈接router-link標簽

在vue項目中&#xff0c;使用elementui 框架&#xff0c;做一個后臺管理系統 在寫左邊菜單&#xff0c;菜用了&#xff0c;elementui 提供的組件 &#xff0c; el-menu 組件。但是組件沒有鏈接&#xff0c;而我們知道添加鏈接使用router-link標簽代碼如下&#xff1a; <el-…

使用fastjson的parseObject方法將json字符串轉換成Map 或者List

fastjson 轉換成map HashMap<String,String> map JSON.parseObject(jsonStr,new TypeReference<HashMap<String,String>>() {}); fastjson 轉換成list List<Person> list new ArrayList<Person>(); list JSON.parseArray(jasonArray.toStri…

【01】《正則表達式必知必會》(已看)(僅存放)

【01】《正則表達式必知必會》 共149頁。掃描版&#xff0c;中文版。Sams Teach Yourselef Regular Expressions in 10 minutesBen Forta著。楊濤 翻譯【】魔芋&#xff1a;這本書已經沒有用了。內容已吸收。內容較為基礎&#xff0c;也很全面。** 附件列表 鏈接&#xff1a;ht…

vue-cli腳手架的.babelrc文件

{// 此項指明&#xff0c;轉碼的規則"presets": [// env項是借助插件babel-preset-env&#xff0c;下面這個配置說的是babel對es6,es7,es8進行轉碼&#xff0c;并且設置amd,commonjs這樣的模塊化文件&#xff0c;不進行轉碼["env", {"modules": …

Java秒殺業務架構設計之路

Java秒殺業務架構設計之路

疑難雜癥,逐個下藥

用戶登陸&#xff08;三次輸錯機會&#xff09;且每次輸錯誤時顯示剩余錯誤次數&#xff08;提示&#xff1a;使用字符串格式化&#xff09; 三次登錄: 1.讓用戶輸入三次的機會,錯一次的時候就要詢問用戶是否要繼續 2.分別判斷用戶名和密碼,如果用戶名錯誤就提示用戶錯誤,如果是…

JS性能分析(測試代碼運行時間)

console.time("timer"); for(var i0;i<10000;i){} console.timeEnd("timer"); timer: 0.274169921875ms轉載于:https://www.cnblogs.com/smzd/p/10647455.html

jsonp原生js跨域拿新浪數據插件封裝【可擴展】

//修改了一個bug,增加了手動釋放垃圾 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><meta http-equiv"X-…

Ansible基本命令

Ansible安裝完成之后就自帶很多命令&#xff0c;其中較常用的有7個&#xff1a; ansibleansible-docansible-galaxyansible-initansible-playbookansible-pullansible-vaultansible ansible -h Usage: ansible <host-pattern> [options] 對本機執行一個命令&#xff1a; …

Java高并發高性能分布式框架從無到有微服務架構設計

Java高并發高性能分布式框架從無到有微服務架構設計

Makefile中幾種賦值

延時變量&#xff0c;只有被使用時才展開定義 :?立即變量&#xff0c;定義時的賦值立即有效 ??條件變量&#xff0c;當變量為空時才賦值 追加賦值轉載于:https://www.cnblogs.com/smzd/p/10695962.html

線程的基本協作和生產者消費者

協作基礎&#xff08;wait/notify&#xff09; Java的根父類是Object&#xff0c;Java在Object類而非Thread類中&#xff0c;定義了一些線程協作的基本方法&#xff0c;使得每個對象都可以調用這些方法&#xff0c;這些方法有兩類&#xff0c;一類是wait&#xff0c;另一類是no…

L1-016 查驗身份證

L1-016 查驗身份證 &#xff08;15 分&#xff09;一個合法的身份證號碼由17位地區、日期編號和順序編號加1位校驗碼組成。校驗碼的計算規則如下&#xff1a; 首先對前17位數字加權求和&#xff0c;權重分配為&#xff1a;{7&#xff0c;9&#xff0c;10&#xff0c;5&#xff…

什么是高并發,如何避免高并發

之前我將高并發的解決方法誤認為是線程或者是隊列可以解決&#xff0c;因為高并發的時候是有很多用戶在訪問&#xff0c;導致出現系統數據不正確、丟失數據現象&#xff0c;所以想到 的是用隊列解決&#xff0c;其實隊列解決的方式也可以處理&#xff0c;比如我們在競拍商品、轉…

.sync 修飾符的理解

正常 子組件&#xff1a; this.$emit(update:title, newTitle)父組件&#xff1a; <text-documentv-bind:title"doc.title"v-on:update:title"doc.title $event" ></text-document>簡潔&#xff1a; <text-document v-bind:title.sync&quo…

L1-025 正整數A+B

題的目標很簡單&#xff0c;就是求兩個正整數A和B的和&#xff0c;其中A和B都在區間[1,1000]。稍微有點麻煩的是&#xff0c;輸入并不保證是兩個正整數。 輸入格式&#xff1a; 輸入在一行給出A和B&#xff0c;其間以空格分開。問題是A和B不一定是滿足要求的正整數&#xff0c;…