<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          深入了解 String、StringBuilder 和 StringBuffer 三兄弟

          共 3325字,需瀏覽 7分鐘

           ·

          2020-07-25 00:22

          a4cda20853b8391f01719c4a97f703ca.webp

          1

          ?碎碎念?



          這是一道老生常談的問(wèn)題了,字符串是不僅是 Java 中非常重要的一個(gè)對(duì)象,它在其他語(yǔ)言中也存在。比如 C++、Visual Basic、C# 等。字符串使用 String 來(lái)表示,字符串一旦被創(chuàng)建出來(lái)就不會(huì)被修改,當(dāng)你想修改 StringBuffer 或者是 StringBuilder,出于效率的考量,雖然 String 可以通過(guò) + 來(lái)創(chuàng)建多個(gè)對(duì)象達(dá)到字符串拼接的效果,但是這種拼接的效率相比 StringBuffer 和 StringBuilder,那就是心有余而力不足了。本篇文章我們一起來(lái)深入了解一下這三個(gè)對(duì)象。


          a4cda20853b8391f01719c4a97f703ca.webp

          2

          ?簡(jiǎn)單認(rèn)識(shí)這三個(gè)對(duì)象?


          String

          String 表示的就是 Java 中的字符串,我們?nèi)粘i_(kāi)發(fā)用到的使用 "" 雙引號(hào)包圍的數(shù)都是字符串的實(shí)例。String 類其實(shí)是通過(guò) char 數(shù)組來(lái)保存字符串的。下面是一個(gè)典型的字符串的聲明

          String?s?=?"abc";

          上面你創(chuàng)建了一個(gè)名為 abc 的字符串。

          字符串是恒定的,一旦創(chuàng)建出來(lái)就不會(huì)被修改,怎么理解這句話?我們可以看下 String 源碼的聲明

          9a513a7892c31aefa8b0915cf413bfe6.webp

          告訴我你看到了什么?String 對(duì)象是由final 修飾的,一旦使用 final 修飾的類不能被繼承、方法不能被重寫(xiě)、屬性不能被修改。而且 String 不只只有類是 final 的,它其中的方法也是由 final 修飾的,換句話說(shuō),Sring 類就是一個(gè)典型的 Immutable 類。也由于 String ?的不可變性,類似字符串拼接、字符串截取等操作都會(huì)產(chǎn)生新的 Strign 對(duì)象。

          所以請(qǐng)你告訴我下面

          String?s1?=?"aaa";
          String?s2?=?"bbb"?+?"ccc";
          String?s3?=?s1?+?"bbb";
          String?s4?=?new?String("aaa");

          分別創(chuàng)建了幾個(gè)對(duì)象?

          • 首先第一個(gè)問(wèn)題,s1 創(chuàng)建了幾個(gè)對(duì)象。字符串在創(chuàng)建對(duì)象時(shí),會(huì)在常量池中看有沒(méi)有 aaa 這個(gè)字符串;如果沒(méi)有此時(shí)還會(huì)在常量池中創(chuàng)建一個(gè);如果有則不創(chuàng)建。我們默認(rèn)是沒(méi)有的情況,所以會(huì)創(chuàng)建一個(gè)對(duì)象。下同。
          • 那么 s2 創(chuàng)建了幾個(gè)對(duì)象呢?是兩個(gè)對(duì)象還是一個(gè)對(duì)象?我們可以使用 javap -c 看一下反匯編代碼
          public class com.sendmessage.api.StringDemo {
          public com.sendmessage.api.StringDemo();
          Code:
          0: aload_0
          1: invokespecial #1 // 執(zhí)行對(duì)象的初始化方法
          4: return

          public static void main(java.lang.String[]);
          Code:
          0: ldc #2 // 將 String aaa 執(zhí)行入棧操作
          2: astore_1 # pop出棧引用值,將其(引用)賦值給局部變量表中的變量 s1
          3: ldc #3 // String bbbccc
          5: astore_2
          6: return
          }

          編譯器做了優(yōu)化 String s2 = "bbb" + "ccc" 會(huì)直接被優(yōu)化為 bbbccc。也就是直接創(chuàng)建了一個(gè) bbbccc 對(duì)象。

          javap 是 jdk 自帶的反匯編工具。它的作用就是根據(jù) class 字節(jié)碼文件,反匯編出當(dāng)前類對(duì)應(yīng)的 code 區(qū)(匯編指令)、本地變量表、異常表和代碼行偏移量映射表、常量池等等信息。

          javap -c 就是對(duì)代碼進(jìn)行反匯編操作。

          • 下面來(lái)看 s3,s3 創(chuàng)建了幾個(gè)對(duì)象呢?是一個(gè)還是兩個(gè)?還是有其他選項(xiàng)?我們使用 javap -c 來(lái)看一下

          350daf5a92b33361321c0e54d85d0320.webp

          我們可以看到,s3 執(zhí)行 + 操作會(huì)創(chuàng)建一個(gè) StringBuilder 對(duì)象然后執(zhí)行初始化。執(zhí)行 + 號(hào)相當(dāng)于是執(zhí)行 new StringBuilder.append() 操作。所以

          String?s3?=?s1?+?"bbb";

          ==
          ??
          String?s3?=?new?StringBuilder().append(s1).append("bbb").toString();

          //?Stringbuilder.toString()?方法也會(huì)創(chuàng)建一個(gè)?String?

          public?String?toString()?{
          ??//?Create?a?copy,?don't?share?the?array
          ??return?new?String(value,?0,?count);
          }

          所以 s3 執(zhí)行完成后,相當(dāng)于創(chuàng)建了 3 個(gè)對(duì)象。

          • 下面來(lái)看 s4 創(chuàng)建了幾個(gè)對(duì)象,在創(chuàng)建這個(gè)對(duì)象時(shí)因?yàn)槭褂昧?new 關(guān)鍵字,所以肯定會(huì)在堆中創(chuàng)建一個(gè)對(duì)象。然后會(huì)在常量池中看有沒(méi)有 aaa 這個(gè)字符串;如果沒(méi)有此時(shí)還會(huì)在常量池中創(chuàng)建一個(gè);如果有則不創(chuàng)建。所以可能是創(chuàng)建一個(gè)或者兩個(gè)對(duì)象,但是一定存在兩個(gè)對(duì)象。

          說(shuō)完了 String 對(duì)象,我們?cè)賮?lái)說(shuō)一下 StringBuilder 和 StringBuffer 對(duì)象。

          上面的 String 對(duì)象竟然和 StringBuilder 產(chǎn)生了千絲萬(wàn)縷的聯(lián)系。不得不說(shuō) StringBuilder 是一個(gè)牛逼的對(duì)象。String 對(duì)象底層是使用了 StringBuilder 對(duì)象的 append 方法進(jìn)行字符串拼接的,不由得對(duì) StringBuilder 心生敬意。

          不由得我們想要真正認(rèn)識(shí)一下這個(gè) StringBuilder 大佬,但是在認(rèn)識(shí)大佬前,還有一個(gè)大 boss 就是 StringBuffer 對(duì)象,這也是你不得不跨越的鴻溝。

          StringBuffer

          StringBuffer 對(duì)象 代表一個(gè)可變的字符串序列,當(dāng)一個(gè) StringBuffer 被創(chuàng)建以后,通過(guò) StringBuffer 的一系列方法可以實(shí)現(xiàn)字符串的拼接、截取等操作。一旦通過(guò) StringBuffer 生成了最終想要的字符串后,就可以調(diào)用其 toString 方法來(lái)生成一個(gè)新的字符串。例如

          StringBuffer?b?=?new?StringBuffer("111");
          b.append("222");
          System.out.println(b);

          我們上面提到 + 操作符連接兩個(gè)字符串,會(huì)自動(dòng)執(zhí)行 toString() 方法。那你猜 StringBuffer.append 方法會(huì)自動(dòng)調(diào)用嗎?直接看一下反匯編代碼不就完了么?

          5715ba62a7225a60fc0826fa4f886e96.webp

          上圖左邊是手動(dòng)調(diào)用 toString 方法的代碼,右圖是沒(méi)有調(diào)用 toString 方法的代碼,可以看到,toString() 方法不像 + 一樣自動(dòng)被調(diào)用。

          StringBuffer 是線程安全的,我們可以通過(guò)它的源碼可以看出

          5877c6e4bc7a7c1df311df88a33db6dc.webp

          StringBuffer 在字符串拼接上面直接使用 synchronized 關(guān)鍵字加鎖,從而保證了線程安全性。

          StringBuilder

          最后來(lái)認(rèn)識(shí)大佬了,StringBuilder 其實(shí)是和 StringBuffer 幾乎一樣,只不過(guò) StringBuilder 是非線程安全的。并且,為什么 + 號(hào)操作符使用 StringBuilder 作為拼接條件而不是使用 StringBuffer 呢?我猜測(cè)原因是加鎖是一個(gè)比較耗時(shí)的操作,而加鎖會(huì)影響性能,所以 String 底層使用 StringBuilder 作為字符串拼接。

          ecb025c43b8d901cf755750ff2332da3.webp


          a4cda20853b8391f01719c4a97f703ca.webp

          3

          ?理解 String、StringBuilder、StringBuffer


          我們上面說(shuō)到,使用 + 連接符時(shí),JVM 會(huì)隱式創(chuàng)建 StringBuilder 對(duì)象,這種方式在大部分情況下并不會(huì)造成效率的損失,不過(guò)在進(jìn)行大量循環(huán)拼接字符串時(shí)則需要注意。如下這段代碼

          String?s?=?"aaaa";
          for?(int?i?=?0;?i?100000
          ;?i++)?{
          ????s?+=?"bbb";
          }

          這是一段很普通的代碼,只不過(guò)對(duì)字符串 s 進(jìn)行了 + 操作,我們通過(guò)反編譯代碼來(lái)看一下。

          //?經(jīng)過(guò)反編譯后
          String?s?=?"aaa";
          for(int?i?=?0;?i?10000
          ;?i++)?{
          ?????s?=?(new?StringBuilder()).append(s).append("bbb").toString();????
          }

          你能看出來(lái)需要注意的地方了嗎?在每次進(jìn)行循環(huán)時(shí),都會(huì)創(chuàng)建一個(gè) StringBuilder 對(duì)象,每次都會(huì)把一個(gè)新的字符串元素 bbb 拼接到 aaa 的后面,所以,執(zhí)行幾次后的結(jié)果如下

          68b5377067d054cd3babceb041e230f2.webp

          每次都會(huì)創(chuàng)建一個(gè) StringBuilder ,并把引用賦給 StringBuilder 對(duì)象,因此每個(gè) StringBuilder 對(duì)象都是強(qiáng)引用, 這樣在創(chuàng)建完畢后,內(nèi)存中就會(huì)多了很多 StringBuilder 的無(wú)用對(duì)象。了解更多關(guān)于引用的知識(shí),請(qǐng)看

          小心點(diǎn),別被當(dāng)成垃圾回收了。

          這樣由于大量 StringBuilder 創(chuàng)建在堆內(nèi)存中,肯定會(huì)造成效率的損失,所以在這種情況下建議在循環(huán)體外創(chuàng)建一個(gè) StringBuilder 對(duì)象調(diào)用 append()方法手動(dòng)拼接。

          例如

          StringBuilder?builder?=?new?StringBuilder("aaa");
          for?(int?i?=?0;?i?10000
          ;?i++)?{
          ????builder.append("bbb");
          }
          builder.toString();

          這段代碼中,只會(huì)創(chuàng)建一個(gè) builder 對(duì)象,每次循環(huán)都會(huì)使用這個(gè) builder 對(duì)象進(jìn)行拼接,因此提高了拼接效率。

          從設(shè)計(jì)角度理解

          我們前面說(shuō)過(guò),String 類是典型的 Immutable 不可變類實(shí)現(xiàn),保證了線程安全性,所有對(duì) String 字符串的修改都會(huì)構(gòu)造出一個(gè)新的 String 對(duì)象,由于 String 的不可變性,不可變對(duì)象在拷貝時(shí)不需要額外的復(fù)制數(shù)據(jù)。

          String 在 JDK1.6 之后提供了 intern() 方法,intern 方法是一個(gè) native 方法,它底層由 C/C++ 實(shí)現(xiàn),intern 方法的目的就是為了把字符串緩存起來(lái),在 JDK1.6 中卻不推薦使用 intern 方法,因?yàn)?JDK1.6 把方法區(qū)放到了永久代(Java 堆的一部分),永久代的空間是有限的,除了 Fullgc 外,其他收集并不會(huì)釋放永久代的存儲(chǔ)空間。JDK1.7 將字符串常量池移到了堆內(nèi)存 中,

          下面我們來(lái)看一段代碼,來(lái)認(rèn)識(shí)一下 intern 方法

          public?static?void?main(String[]?args)?{

          ??String?a?=?new?String("ab");
          ??String?b?=?new?String("ab");
          ??String?c?=?"ab";
          ??String?d?=?"a";
          ??String?e?=?new?String("b");
          ??String?f?=?d?+?e;

          ??System.out.println(a.intern()?==?b);
          ??System.out.println(a.intern()?==?b.intern());
          ??System.out.println(a.intern()?==?c);
          ??System.out.println(a.intern()?==?f);

          }

          上述的執(zhí)行結(jié)果是什么呢?我們先把答案貼出來(lái),以防心急的同學(xué)想急于看到結(jié)果,他們的答案是

          false true true false

          和你預(yù)想的一樣嗎?為什么會(huì)這樣呢?我們先來(lái)看一下 intern 方法的官方解釋

          0fea06f131f82fd6471a64eb94bbadc5.webp

          這里你需要知道 JVM 的內(nèi)存模型

          0d7636bc6dfc3f5fb1132263a75ca2de.webp

          • 虛擬機(jī)棧 : Java 虛擬機(jī)棧是線程私有的數(shù)據(jù)區(qū),Java 虛擬機(jī)棧的生命周期與線程相同,虛擬機(jī)棧也是局部變量的存儲(chǔ)位置。方法在執(zhí)行過(guò)程中,會(huì)在虛擬機(jī)棧種創(chuàng)建一個(gè) 棧幀(stack frame)
          • 本地方法棧: 本地方法棧也是線程私有的數(shù)據(jù)區(qū),本地方法棧存儲(chǔ)的區(qū)域主要是 Java 中使用 native 關(guān)鍵字修飾的方法所存儲(chǔ)的區(qū)域
          • 程序計(jì)數(shù)器:程序計(jì)數(shù)器也是線程私有的數(shù)據(jù)區(qū),這部分區(qū)域用于存儲(chǔ)線程的指令地址,用于判斷線程的分支、循環(huán)、跳轉(zhuǎn)、異常、線程切換和恢復(fù)等功能,這些都通過(guò)程序計(jì)數(shù)器來(lái)完成。
          • 方法區(qū):方法區(qū)是各個(gè)線程共享的內(nèi)存區(qū)域,它用于存儲(chǔ)虛擬機(jī)加載的 類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。
          • :堆是線程共享的數(shù)據(jù)區(qū),堆是 JVM 中最大的一塊存儲(chǔ)區(qū)域,所有的對(duì)象實(shí)例都會(huì)分配在堆上
          • 運(yùn)行時(shí)常量池:運(yùn)行時(shí)常量池又被稱為 Runtime Constant Pool,這塊區(qū)域是方法區(qū)的一部分,它的名字非常有意思,它并不要求常量一定只有在編譯期才能產(chǎn)生,也就是并非編譯期間將常量放在常量池中,運(yùn)行期間也可以將新的常量放入常量池中,String 的 intern 方法就是一個(gè)典型的例子。

          在 JDK 1.6 及之前的版本中,常量池是分配在方法區(qū)中永久代(Parmanent Generation)內(nèi)的,而永久代和 Java 堆是兩個(gè)完全分開(kāi)的區(qū)域。如果字符串常量池中已經(jīng)包含一個(gè)等于此 String 對(duì)象的字符串,則返回常量池中這個(gè)字符串的 String 對(duì)象;否則,將此 String 對(duì)象包含的字符串添加到常量池中,并且返回此 String 對(duì)象的引用。

          一些人把方法區(qū)稱為永久代,這種說(shuō)法不準(zhǔn)確,僅僅是 Hotspot 虛擬機(jī)設(shè)計(jì)團(tuán)隊(duì)選擇使用永久代來(lái)實(shí)現(xiàn)方法區(qū)而已。

          從JDK 1.7開(kāi)始去永久代,字符串常量池已經(jīng)被轉(zhuǎn)移至 Java 堆中,開(kāi)發(fā)人員也對(duì) intern 方法做了一些修改。因?yàn)樽址A砍睾?new 的對(duì)象都存于 Java 堆中,為了優(yōu)化性能和減少內(nèi)存開(kāi)銷(xiāo),當(dāng)調(diào)用 intern 方法時(shí),如果常量池中已經(jīng)存在該字符串,則返回池中字符串;否則直接存儲(chǔ)堆中的引用,也就是字符串常量池中存儲(chǔ)的是指向堆里的對(duì)象。

          所以我們對(duì)上面的結(jié)論進(jìn)行分析

          String?a?=?new?String("ab");
          String?b?=?new?String("ab");

          System.out.println(a.intern()?==?b);

          輸出什么?false,為什么呢?畫(huà)一張圖你就明白了(圖畫(huà)的有些問(wèn)題,棧應(yīng)該是后入先出,所以 b 應(yīng)該在 a 上面,不過(guò)不影響效果)

          32a8f35ccbdb20343beacccec7fcb3b2.webp

          a.intern 返回的是常量池中的 ab,而 b 是直接返回的是堆中的 ab。地址不一樣,肯定輸出 false

          所以第二個(gè)

          System.out.println(a.intern()?==?b.intern());

          也就沒(méi)問(wèn)題了吧,它們都返回的是字符串常量池中的 ab,地址相同,所以輸出 true

          9c8dced14cb33a89893a0352f9dd9e47.webp

          然后來(lái)看第三個(gè)

          System.out.println(a.intern()?==?c);

          圖示如下

          4fa10202a89f093112d6a67a21ad81bb.webp

          a 不會(huì)變,因?yàn)槌A砍刂幸呀?jīng)有了 ab ,所以 c 不會(huì)再創(chuàng)建一個(gè) ab 字符串,這是編譯器做的優(yōu)化,為了提高效率。

          下面來(lái)看最后一個(gè)

          System.out.println(a.intern()?==?f);

          2ccb5d6f419a12ef9c26b4f207a6e871.webp

          String

          首先來(lái)看一下 String 類在繼承樹(shù)的什么位置、實(shí)現(xiàn)了什么接口、父類是誰(shuí),這是源碼分析的幾大重要因素。

          String 沒(méi)有繼承任何接口,不過(guò)實(shí)現(xiàn)了三個(gè)接口,分別是 Serializable、Comparable、CharSequence 接口

          • Serializable :這個(gè)序列化接口沒(méi)有任何方法和域,僅用于標(biāo)識(shí)序列化的語(yǔ)意。
          • Comparable:實(shí)現(xiàn)了 Comparable 的接口可用于內(nèi)部比較兩個(gè)對(duì)象的大小
          • CharSequence:字符串序列接口,CharSequence 是一個(gè)可讀的 char 值序列,提供了 length(), charAt(int index), subSequence(int start, int end) 等接口,StringBuilder 和 StringBuffer 也繼承了這個(gè)接口

          重要屬性

          字符串是什么,字!符!串!你品,你細(xì)品。你會(huì)發(fā)現(xiàn)它就是一連串字符組成的串。

          也就是說(shuō)

          String?str?=?"abc";?

          //?===?

          char?data[]?=?{'a',?'b',?'c'};
          String?str?=?new?String(data);

          原來(lái)這么回事啊!

          所以,String 中有一個(gè)用于存儲(chǔ)字符的 char 數(shù)組 value[],這個(gè)數(shù)組存儲(chǔ)了每個(gè)字符。另外一個(gè)就是 hash 屬性,它用于緩存字符串的哈希碼。因?yàn)?String 經(jīng)常被用于比較,比如在 HashMap 中。如果每次進(jìn)行比較都重新計(jì)算其 hashcode 的值的話,那無(wú)疑是比較麻煩的,而保存一個(gè) hashcode 的緩存無(wú)疑能優(yōu)化這樣的操作。

          b9c9cc5af740eb8b4ad7bb1d41890d16.webp

          String 可以通過(guò)許多途徑創(chuàng)建,也可以根據(jù) Stringbuffer 和 StringBuilder 進(jìn)行創(chuàng)建。

          bede62db5d149ccc58cff48cd595790b.webp

          畢竟我們本篇文章探討的不是源碼分析的文章,所以涉及到的源碼不會(huì)很多。

          除此之外,String 還提供了一些其他方法

          • charAt :返回指定位置上字符的值

          • getChars: 復(fù)制 String 中的字符到指定的數(shù)組

          • equals: 用于判斷 String 對(duì)象的值是否相等

          • indexOf : 用于檢索字符串

          • substring: 對(duì)字符串進(jìn)行截取

          • concat: 用于字符串拼接,效率高于 +

          • replace:用于字符串替換

          • match:正則表達(dá)式的字符串匹配

          • contains: 是否包含指定字符序列

          • split: 字符串分割

          • join: ?字符串拼接

          • trim: 去掉多余空格

          • toCharArray: 把 String 對(duì)象轉(zhuǎn)換為字符數(shù)組

          • valueOf: 把對(duì)象轉(zhuǎn)換為字符串

          StringBuilder

          StringBuilder 類表示一個(gè)可變的字符序列,我們知道,StringBuilder 是非線程安全的容器,一般適用于單線程場(chǎng)景中的字符串拼接操作,下面我們就來(lái)從源碼角度看一下 StringBuilder

          首先我們來(lái)看一下 StringBuilder 的定義

          public?final?class?StringBuilder
          ????extends?AbstractStringBuilder
          ????implements?java.io.Serializable,?CharSequence?
          {...}

          StringBuilder 被 final 修飾,表示 StringBuilder 是不可被繼承的,StringBuilder 類繼承于 AbstractStringBuilder類。實(shí)際上,AbstractStringBuilder 類具體實(shí)現(xiàn)了可變字符序列的一系列操作,比如:append()、insert()、delete()、replace()、charAt() 方法等。

          StringBuilder 實(shí)現(xiàn)了 2 個(gè)接口

          • Serializable 序列化接口,表示對(duì)象可以被序列化。
          • CharSequence 字符序列接口,提供了幾個(gè)對(duì)字符序列進(jìn)行只讀訪問(wèn)的方法,例如 ength()、charAt()、subSequence()、toString() 方法等。

          StringBuilder 使用 AbstractStringBuilder 類中的兩個(gè)變量作為元素

          char[]?value;?//?存儲(chǔ)字符數(shù)組

          int?count;?//?字符串使用的計(jì)數(shù)

          StringBuffer

          StringBuffer 也是繼承于 AbstractStringBuilder ,使用 value 和 count 分別表示存儲(chǔ)的字符數(shù)組和字符串使用的計(jì)數(shù),StringBuffer 與 StringBuilder 最大的區(qū)別就是 StringBuffer 可以在多線程場(chǎng)景下使用,StringBuffer 內(nèi)部有大部分方法都加了 synchronized 鎖。在單線程場(chǎng)景下效率比較低,因?yàn)橛墟i的開(kāi)銷(xiāo)。

          StringBuilder 和 StringBuffer 的擴(kuò)容問(wèn)題

          我相信這個(gè)問(wèn)題很多同學(xué)都沒(méi)有注意到吧,其實(shí) StringBuilder 和 StringBuffer 存在擴(kuò)容問(wèn)題,先從 StringBuilder 開(kāi)始看起

          首先先注意一下 StringBuilder 的初始容量

          public?StringBuilder()?{
          ??super(16);
          }

          StringBuilder 的初始容量是 16,當(dāng)然也可以指定 StringBuilder 的初始容量。

          在調(diào)用 append 拼接字符串,會(huì)調(diào)用 AbstractStringBuilder 中的 append 方法

          public?AbstractStringBuilder?append(String?str)?{
          ??if?(str?==?null)
          ????return?appendNull();
          ??int?len?=?str.length();
          ??ensureCapacityInternal(count?+?len);
          ??str.getChars(0,?len,?value,?count);
          ??count?+=?len;
          ??return?this;
          }

          上面代碼中有一個(gè) ensureCapacityInternal 方法,這個(gè)就是擴(kuò)容方法,我們跟進(jìn)去看一下

          private?void?ensureCapacityInternal(int?minimumCapacity)?{
          ??//?overflow-conscious?code
          ??if?(minimumCapacity?-?value.length?>?0)?{
          ????value?=?Arrays.copyOf(value,
          ??????????????????????????newCapacity(minimumCapacity));
          ??}
          }

          這個(gè)方法會(huì)進(jìn)行判斷,minimumCapacity 就是字符長(zhǎng)度 + 要拼接的字符串長(zhǎng)度,如果拼接后的字符串要比當(dāng)前字符長(zhǎng)度大的話,會(huì)進(jìn)行數(shù)據(jù)的復(fù)制,真正擴(kuò)容的方法是在 newCapacity

          private?int?newCapacity(int?minCapacity)?{
          ??//?overflow-conscious?code
          ??int?newCapacity?=?(value.length?<1
          )?+?2;
          ??if?(newCapacity?-?minCapacity?0)?{
          ????newCapacity?=?minCapacity;
          ??}
          ??return?(newCapacity?<=?0?||?MAX_ARRAY_SIZE?-?newCapacity?0)
          ??????hugeCapacity(minCapacity)
          ????:?newCapacity;
          }

          擴(kuò)容后的字符串長(zhǎng)度會(huì)是原字符串長(zhǎng)度增加一倍 + 2,如果擴(kuò)容后的長(zhǎng)度還比拼接后的字符串長(zhǎng)度小的話,那就直接擴(kuò)容到它需要的長(zhǎng)度 ?newCapacity = minCapacity,然后再進(jìn)行數(shù)組的拷貝。


          a4cda20853b8391f01719c4a97f703ca.webp

          4

          ?總結(jié)



          本篇文章主要描述了 String 、StringBuilder 和 StringBuffer 的主要特性,String、StringBuilder 和 StringBuffer 的底層構(gòu)造是怎樣的,以及 String 常量池的優(yōu)化、StringBuilder 和 StringBuffer 的擴(kuò)容特性等。

          如果有錯(cuò)誤的地方,還請(qǐng)大佬們提出寶貴意見(jiàn)。


          ? ? ? ?a780fe53203de9cd8cfe926d00519729.webp???7 張圖讓你搞懂 Java 集合框架教你寫(xiě) Bug,常見(jiàn)的 OOM 異常分析
          搞定計(jì)算機(jī)網(wǎng)絡(luò),這些問(wèn)題還沒(méi)有我答不出來(lái)的

          覺(jué)得不錯(cuò),點(diǎn)個(gè)在看~

          e01282155f056483f0133abbe7f87f37.webp
          瀏覽 29
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  亚洲高清超级无码在线视频观看 | 天天干人人 | 国产对白视频 | 韩日一级无码 | 色黄在线视频 |