Java 中的 String、StringBuilder嘿般、StringBuffer 的區(qū)別

目錄

一赛糟、是什么?

  • String 不可變字符序列

String 是字符串常量砸逊,其對(duì)象一旦創(chuàng)建之后該對(duì)象是不可更改的璧南, 因此在每次對(duì) String 類型進(jìn)行改變的時(shí)候其實(shí)都等同于生成了一個(gè)新的 String 對(duì)象,然后將指針指向新的 String 對(duì)象痹兜,所以經(jīng)常改變內(nèi)容的字符串最好不要用 String 穆咐,因?yàn)槊看紊尚聦?duì)象都會(huì)開辟新的內(nèi)存空間,當(dāng)內(nèi)存中無引用對(duì)象多了以后字旭, JVM 的 GC 就會(huì)開始工作,那速度一定是相當(dāng)慢的崖叫,對(duì)系統(tǒng)性能產(chǎn)生影響遗淳。
String 這個(gè)類很特殊,特殊在于 JVM 專門為它作了某些處理:在 JVM 中存在一個(gè)字符串常量池心傀,其中存有很多 String 對(duì)象屈暗,并且可以被共享使用。當(dāng)創(chuàng)建一個(gè)字符串常量時(shí)脂男,例如 String str = “Chittyo”; 會(huì)首先在字符串常量池中查找是否存在相同的字符串定義养叛,若已經(jīng)定義,則直接引用其定義宰翅,此時(shí)不需要?jiǎng)?chuàng)建新的對(duì)象弃甥;若沒有定義,則需要?jiǎng)?chuàng)建對(duì)象汁讼,然后把它加入到字符串常量池中淆攻,再將他的引用返回阔墩。由于字符串是不可變類,一旦創(chuàng)建好了就不可修改瓶珊,因此字符串對(duì)象可以被共享而且不會(huì)引起程序的混亂啸箫。

  • StringBuilder 可變字符序列、效率高伞芹、非線程安全

java.lang.StringBuilder 是 Java 5.0 新增的可變的字符序列忘苛。此類提供一個(gè)與 StringBuffer 兼容的 API,但不保證同步唱较。該類被設(shè)計(jì)用作 StringBuffer 的一個(gè)簡(jiǎn)易替換扎唾,用在字符串緩沖區(qū)被單個(gè)線程使用的時(shí)候(這種情況很普遍)。如果可能绊汹,建議優(yōu)先采用該類稽屏,因?yàn)樵诖蠖鄶?shù)實(shí)現(xiàn)中,它比 StringBuffer 要快西乖。兩者的方法基本相同狐榔。

  • StringBuffer 可變字符序列、效率低获雕、線程安全

Java.lang.StringBuffer 是線程安全的可變字符序列薄腻。一個(gè)類似于 String 的字符串緩沖區(qū)。雖然在任意時(shí)間點(diǎn)上它都包含某種特定的字符序列届案,但通過某些方法調(diào)用可以改變?cè)撔蛄械拈L(zhǎng)度和內(nèi)容庵楷。可將字符串緩沖區(qū)安全地用于多個(gè)線程楣颠【∨Γ可以在必要時(shí)對(duì)這些方法進(jìn)行同步,因此任意特定實(shí)例上的所有操作就好像是以串行順序發(fā)生的童漩,該順序與所涉及的每個(gè)線程進(jìn)行的方法調(diào)用順序一致弄贿。每個(gè)字符串生成器都有一定的容量,只要字符串生成器包含的字符序列的長(zhǎng)度沒有超出此容量矫膨,就無需分配新的內(nèi)容緩沖區(qū)差凹。如果內(nèi)容緩沖區(qū)溢出,則此容量自動(dòng)增大侧馅。
StringBuffer 上的主要操作是 append()insert() 方法危尿,可重載這些方法,以接受任意類型的數(shù)據(jù)馁痴。每個(gè)方法都能有效地將給定的數(shù)據(jù)轉(zhuǎn)換成字符串谊娇,然后將該字符串的字符 追加 or 插入 到字符串緩沖區(qū)中。append() 方法始終將這些字符添加到緩沖區(qū)的末端弥搞;而 insert() 方法則在指定的點(diǎn)添加字符邮绿。

二渠旁、區(qū)別是?

主要存在以下兩個(gè)方面的區(qū)別:運(yùn)行速度船逮、線程安全顾腊。

1. 運(yùn)行速度(執(zhí)行速度)

運(yùn)行速度的快慢:StringBuilder > StringBuffer > String。

為什么 String 最慢呢挖胃?因?yàn)?String 為字符串常量杂靶,而 StringBuilder 和 StringBuffer 均為字符串變量,即 String 對(duì)象一旦創(chuàng)建之后該對(duì)象是不可更改的酱鸭,但后兩者的對(duì)象是變量吗垮,是可以更改的。
一言不合上代碼凹髓,舉個(gè)栗子:

String str = "Chitty";
System.out.println(str);
str = str + "o";
System.out.println(str);

運(yùn)行這段代碼烁登,會(huì)先輸出 “Chitty”,后輸出 “Chittyo”蔚舀《祝看著像是 str 這個(gè)對(duì)象被更改了,實(shí)則不然赌躺,假象而已狼牺。


JVM 對(duì)于這幾行代碼是這樣處理的,(嚴(yán)謹(jǐn)起見礼患,假設(shè)上述代碼中的字符串都是第一次創(chuàng)建是钥,在字符串常量池中找不到),首先在堆內(nèi)存中新建了一塊內(nèi)存空間缅叠,分配給第一行創(chuàng)建的 String 對(duì)象 str悄泥,并把 “Chitty” 賦值給 str,然后在第三行中肤粱,JVM 在堆內(nèi)存中又創(chuàng)建了兩份內(nèi)存空間码泞,用來存放 “o” 和最終的 String 對(duì)象 str。所以狼犯,第一行的 str 實(shí)際上并沒有被更改,即之前說的 String 對(duì)象一旦創(chuàng)建之后就不可更改了领铐。而原來第一行的 str(“Chitty”) 以及 第三行中新建的 “o” 的內(nèi)存空間悯森,并不會(huì)即時(shí)就被 JVM 的垃圾回收機(jī)制(GC)給回收掉,GC 的時(shí)機(jī)是 JVM 在某個(gè)時(shí)候绪撵,才開始執(zhí)行的瓢姻,所以并一定會(huì)明顯的由于新開辟內(nèi)存空間,且回收內(nèi)存音诈,引起 String 速度變慢幻碱。嚴(yán)謹(jǐn)來說绎狭,在大量的 String 拼接操作出現(xiàn)的時(shí)候,JVM 由于開辟內(nèi)存空間過多褥傍,導(dǎo)致內(nèi)存緊張儡嘶,基本實(shí)時(shí)進(jìn)行 GC,這樣才會(huì)引起速度變慢恍风。Java 中對(duì) String 對(duì)象進(jìn)行的操作實(shí)際上是一個(gè)不斷創(chuàng)建新的對(duì)象并且適時(shí)將舊的對(duì)象回收的一個(gè)過程蹦狂,這不僅是對(duì)內(nèi)存空間的極大浪費(fèi),也導(dǎo)致了執(zhí)行速度緩慢朋贬。而 StringBuilder 和 StringBuffer 的對(duì)象是變量凯楔,能夠被多次修改,且不產(chǎn)生新的對(duì)象锦募,即不進(jìn)行創(chuàng)建和回收的操作摆屯,所以速度要比 String 快很多。

換個(gè)栗子舉一下:

String str = "Chitty" + "o";
StringBuilder stringBuilder = new StringBuilder().append("Chitty").append("o");
System.out.println(str);
System.out.println(stringBuilder.toString());

這樣輸出結(jié)果也是 “Chittyo” 和 “Chittyo”糠亩,但是 String 的速度卻比 StringBuilder 的反應(yīng)速度要快很多虐骑,這是因?yàn)榈?1 行中的操作

String str = "Chitty" + "o";

String str = "Chittyo"; 

是完全一樣的,所以會(huì)很快削解。如若寫成下面這種形式富弦,

String str1 = "Chitty";
String str2 = "o";
String str = str1 + str2;

那么,JVM 就會(huì)像上面說的那樣氛驮,不斷的創(chuàng)建腕柜、回收對(duì)象來進(jìn)行這個(gè)操作了。速度就會(huì)很慢矫废。

由于 StringBuilder 相較于 StringBuffer 有速度優(yōu)勢(shì)盏缤,所以多數(shù)情況下建議使用 StringBuilder 類。然而在應(yīng)用程序要求線程安全的情況下蓖扑,則必須使用 StringBuffer 類唉铜。 下面我們來看下線程安全方面的區(qū)別。

2. 線程安全

StringBuilder 是線程不安全的律杠,而 StringBuffer 是線程安全的潭流。
如果一個(gè) StringBuffer 對(duì)象在字符串緩沖區(qū)被多個(gè)線程使用時(shí),StringBuffer 中很多方法可以帶有 synchronized 關(guān)鍵字柜去,所以可以保證線程是安全的灰嫉。但 StringBuilder 的方法則沒有該關(guān)鍵字,所以不能保證線程安全嗓奢,有可能會(huì)出現(xiàn)一些錯(cuò)誤的操作讼撒。所以在多線程環(huán)境下操作用 StringBuffer,在單線程環(huán)境下操作,還是建議使用速度比較快的 StringBuilder根盒。

三钳幅、小結(jié)

  • 操作少量的字符串?dāng)?shù)據(jù) 用 String;
  • 單線程下字符緩沖區(qū)中的大量操作 用 StringBuilder(推薦使用)炎滞;
  • 多線程下字符緩沖區(qū)中的大量操作 用 StringBuffer敢艰。

四、加餐

String strA = "Chittyo";
String strB = "Chittyo";
String strC = new String("Chittyo");
String strD = new String("Chittyo");
System.out.println(strA == strB);
System.out.println(strC == strD);

Q:創(chuàng)建 String 對(duì)象的兩種方式的區(qū)別是什么厂榛?
A:首先看一下打印結(jié)果:第五行打印 true 盖矫;第六行打印 false

分析:我們知道 Java 的 8 種基本數(shù)據(jù)類型( int, long, short, double, float, byte, char, boolean)用 == 比較的是變量值击奶,因?yàn)樗麄儧]有地址辈双,只有值。而 String 是引用數(shù)據(jù)類型柜砾,== 比較的是兩個(gè)引用變量的地址湃望。

strC 和 strD 是 new 出來的兩個(gè)完全不同的對(duì)象,引用變量的地址不同痰驱,僅僅是值相等证芭。可類比記憶:兩個(gè)人僅僅是名字相同担映。所以第六行打印 false 废士。
來看一下,創(chuàng)建新對(duì)象的過程:

① 執(zhí)行 String strC = new String("Chittyo"); 時(shí)蝇完,JVM 直接創(chuàng)建一個(gè)新的對(duì)象并讓 strC 指向該對(duì)象官硝;
② 執(zhí)行 String strD = new String("Chittyo"); 時(shí),JVM 再次創(chuàng)建一個(gè)新的對(duì)象并讓 strD 指向該對(duì)象短蜕;
③ 所以 strCstrD 指向不同的對(duì)象氢架,即引用變量的地址不同。

那么 strA == strB 嗎朋魔?strA岖研、strB 并不是通過 new 的方式創(chuàng)建的,所以他們的地址取決于后面所賦的值警检。Java 中孙援,普通字符串存儲(chǔ)在字符串常量池中,字符串常量池目前位于堆內(nèi)存中( JDK 1.8扇雕,JVM 把字符串常量池移到了堆內(nèi)存中)赃磨。
再來瞄一眼,直接賦值過程:

① 執(zhí)行 String strA = "Chittyo"; 后洼裤,JVM 在字符串常量池中開辟空間存放一個(gè) “Chittyo” 字符串空間并讓 strA 指向該對(duì)象。
② 執(zhí)行 String strB = "Chittyo"; 時(shí),JVM 會(huì)先檢查字符串常量池中是否已經(jīng)存在了一內(nèi)容為 "Chittyo" 的空間腮鞍,如果存在就直接讓 strB 指向該空間值骇,否則就會(huì)在開辟一個(gè)新的空間存放該字符串。
③ 所以創(chuàng)建 strB 的時(shí)候移国,因?yàn)樽址A砍刂幸呀?jīng)有字符串 "Chittyo" 吱瘩,所以直接讓 strB 指向該空間。相當(dāng)于:String strB = strA;

所以迹缀,從賦值方面來看使碾,此時(shí)的 strA == strB 是成立的,比較的是字符串常量池里的值祝懂。(字符串常量池在堆內(nèi)存中)

在這里插入圖片描述

引用類型指向一個(gè)對(duì)象票摇,指向?qū)ο蟮淖兞渴且米兞俊_@些變量在聲明時(shí)被指定為一個(gè)特定的類型砚蓬。變量一旦聲明后矢门,類型就不能被改變了。對(duì)象灰蛙、數(shù)組都是引用數(shù)據(jù)類型祟剔。所有引用類型的默認(rèn)值都是 null。一個(gè)引用變量可以用來引用任何與之兼容的類型摩梧。

一般對(duì)于對(duì)象物延,比較值是否相等的時(shí)候,都是通過覆寫 equals() 方法和 hashCode() 方法來比較的仅父。String 類型比較不同對(duì)象內(nèi)容是否相同叛薯,應(yīng)該用 equals() ,因?yàn)?== 用于比較引用數(shù)據(jù)類型和基本數(shù)據(jù)類型時(shí)具有不同的功能驾霜。

== 用于基本數(shù)據(jù)類型的比較案训,判斷引用是否指向堆內(nèi)存的同一塊地址。
equals() 用于判斷兩個(gè)變量是否是被同一個(gè)對(duì)象引用粪糙,即堆中的內(nèi)容是否相同强霎,返回值為布爾類型。

String str1 = new String("Chittyo");
String str2 = new String("Chittyo");
String str3 = str1;
System.out.println(str1 == str2);     //false
System.out.println(str1.equals(str2));//true
System.out.println(str1 == str3);     //true
System.out.println(str1.equals(str3); //true

巧記:== 用來比較棧內(nèi)存中的值蓉冈,equals() 用來比較堆內(nèi)存中的值城舞。

JVM 把內(nèi)存劃分成兩種:一種是棧內(nèi)存,一種是堆內(nèi)存寞酿。
 〖叶帷① 在函數(shù)中定義的一些基本數(shù)據(jù)類型的變量和對(duì)象的引用變量(變量名)都在函數(shù)的棧內(nèi)存中分配。
 》サ② 當(dāng)在一段代碼塊定義一個(gè)變量時(shí)拉馋,Java 就在棧中為這個(gè)變量分配內(nèi)存空間,當(dāng)超過變量的作用域后,Java 會(huì)自動(dòng)釋放掉為該變量所分配的內(nèi)存空間煌茴,該內(nèi)存空間可以立即被另作他用随闺。
  ③ 堆內(nèi)存用來存放由 new 創(chuàng)建的對(duì)象(包括由基本類型包裝起來的類:Integer蔓腐、String矩乐、Double 等,實(shí)際上每個(gè)基本類型都有他的包裝類)和數(shù)組回论。

結(jié)尾

本文到這里就結(jié)束了散罕,感謝看到最后的朋友,都看到最后了傀蓉,點(diǎn)個(gè)贊再走啊欧漱,如有不對(duì)之處還請(qǐng)多多指正。
關(guān)注我?guī)憬怄i更多精彩內(nèi)容

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末僚害,一起剝皮案震驚了整個(gè)濱河市硫椰,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌萨蚕,老刑警劉巖靶草,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異岳遥,居然都是意外死亡奕翔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門浩蓉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來派继,“玉大人,你說我怎么就攤上這事捻艳〖菘撸” “怎么了?”我有些...
    開封第一講書人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵认轨,是天一觀的道長(zhǎng)绅络。 經(jīng)常有香客問我,道長(zhǎng)嘁字,這世上最難降的妖魔是什么恩急? 我笑而不...
    開封第一講書人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮纪蜒,結(jié)果婚禮上衷恭,老公的妹妹穿的比我還像新娘。我一直安慰自己纯续,他們只是感情好随珠,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開白布灭袁。 她就那樣靜靜地躺著,像睡著了一般牙丽。 火紅的嫁衣襯著肌膚如雪简卧。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,737評(píng)論 1 305
  • 那天烤芦,我揣著相機(jī)與錄音,去河邊找鬼析校。 笑死构罗,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的智玻。 我是一名探鬼主播遂唧,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼吊奢!你這毒婦竟也來了盖彭?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤页滚,失蹤者是張志新(化名)和其女友劉穎召边,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體裹驰,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡隧熙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了幻林。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片贞盯。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖沪饺,靈堂內(nèi)的尸體忽然破棺而出躏敢,到底是詐尸還是另有隱情,我是刑警寧澤整葡,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布件余,位于F島的核電站,受9級(jí)特大地震影響掘宪,放射性物質(zhì)發(fā)生泄漏蛾扇。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一魏滚、第九天 我趴在偏房一處隱蔽的房頂上張望镀首。 院中可真熱鬧,春花似錦鼠次、人聲如沸更哄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)成翩。三九已至觅捆,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間麻敌,已是汗流浹背栅炒。 一陣腳步聲響...
    開封第一講書人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留术羔,地道東北人赢赊。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像级历,于是被迫代替她去往敵國(guó)和親释移。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355