<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 之間的區(qū)別

          共 9177字,需瀏覽 19分鐘

           ·

          2021-04-22 08:24


          點擊上方“程序員大白”,選擇“星標”公眾號

          重磅干貨,第一時間送達

          String

          初始化字符串:

          String str = "so easy!";

          這是字符串初始化的 “簡介版本”,通常字符串的初始化有三種方式:

          // 1.最常見,簡短
          String str1 = "Hello wrold!";
          // 2.構造函數(shù)初始化
          String str2 = new String("Hello World!");
          // 3.字符數(shù)組初始化
          char[] charArray = {'H''e''l''l''o'' ''W''o''r''l''d'};
          String str3 = new String(charArray);

          然后我們一起看一下 String 類的源碼,再做分析。

          public final class String
              implements java.io.SerializableComparable<String>, CharSequence 
          {
              /** The value is used for character storage. */
              private final char value[];

              /**
               * Initializes a newly created {@code String} object so that it represents
               * an empty character sequence.  Note that use of this constructor is
               * unnecessary since Strings are immutable.
               */

              public String() {
                  this.value = new char[0];
              }

              /**
               * Allocates a new {@code String} so that it represents the sequence of
               * characters currently contained in the character array argument. The
               * contents of the character array are copied; subsequent modification of
               * the character array does not affect the newly created string.
               *
               * @param  value
               *         The initial value of the string
               */

              public String(char value[]) {
                  this.value = Arrays.copyOf(value, value.length);
              }
              
              ...
          }

          private final char value[]; ,可以觀察字符串本身是保存在 char 數(shù)組中的,換句話說,String 對象其實就是一個字符數(shù)組。

          這里需要注意的一件非常重要的事情是,String 類被 final 關鍵字所修飾,public final class String{}, 這意味著 String 是不可變(immutable)類型。

          不可變(immutable) 意味著什么呢?

          String str1 = "Hello World!";
          str1.substring(1,4).concat("cbl").toLowerCase().trim().replace('b''a');
          System.out.println(str1); // Hello World!

          上面這段程序的輸出依舊是 Hello World! 。

          因為字符串是 final 的,所以對 str1 所進行的一系列操作并不會改變 str1 本身。這一系列操作返回的是更改后的新字符串,并不會改變 str1 本身。

          我們可以看一下 concat() 的源碼:

          public String concat(String str) {
              int otherLen = str.length();
              if (otherLen == 0) {
                  return this;
              }
              int len = value.length;
              char buf[] = Arrays.copyOf(value, len + otherLen);
              str.getChars(buf, len);
              return new String(buf, true);
          }

          原始字符串永遠不會更改。而是復制一份,并將要連接的文本被添加到復制的字符串之后,最后返回一個新的字符串。

          我們用一個變量接收一下返回值,然后輸出,發(fā)現(xiàn)返回了一個新的字符串 local

          String str1 = "Hello World!";
          String str2 = str1.substring(3,5).concat("cbl").toLowerCase().trim().replace('b''a');
          System.out.println(str2); // local

          JVM 的原生內(nèi)存模型為:

          方便我們對 String 、StringBuffer 和 StringBuilder 的理解,我們僅關心下面圖示中的內(nèi)存區(qū)域:

          其中 Stack 表示棧區(qū),Heap 表示堆區(qū),String Pool 表示字符串池。

          我們再來看看這兩個字符串:

          String str1 = "algorithm";
          String str2 = "algorithm";

          實例化的字符串 str1str2 的值保存在Java 堆內(nèi)存中,堆內(nèi)存用于為所有 Java 對象動態(tài)分配內(nèi)存。雖然  str1str2 是兩個不同的引用變量,但它們都指向 Java 堆內(nèi)存中的同一塊內(nèi)存位置。

          雖然看起來有兩個不同的 String 對象,但實際上只有一個。str2 從未被實例化為對象,而是引用了在堆內(nèi)存中分配給 str1 的對象。

          這是由于 Java 針對字符串進行優(yōu)化的方式造成的。每次實例化字符串對象時,都會將添加到堆內(nèi)存的值與之前已添加的值進行比較。如果堆內(nèi)存中已存在相等的值,則不會初始化該對象,而是將該值賦給引用變量。

          str1 這樣的常量值保存在一個稱為常量字符串的池中,簡稱 String Pool,其中包含所有的常量字符串。不過,使用關鍵字 new 創(chuàng)建的字符串 “偷偷” 繞過了 String Pool,而是直接存儲在 Java Heap 當中。

          再看一個簡單的例子:

          String str1 = "algorithm";
          String str2 = "algorithm";
          String str3 = new String("algorithm");

          System.out.println(str1 == str2); // true
          System.out.println(str1 == str3); // false
          System.out.println(str1.equals(str2)); // true
          System.out.println(str1.equals(str3)); // false

          因為 str1str2 指向內(nèi)存中的同一對象, str1 == str2 返回為 true ,str1.equals(str2) 也返回 true 。

          因為 str3  使用關鍵字 new 顯式實例化,所以 String Pool 中雖然已存在字符串文本,依舊會使用構造函數(shù)創(chuàng)建一個新對象。

          equals() 方法比較的兩個變量的值,而不是它們所指向的內(nèi)存地址,這就是 str1.equals(str3)str1.equals(str2) 都返回 true 的原因。而 == 判斷的是兩個變量所指向的內(nèi)存地址,比較的是真正意義上的指針操作,所以 str1 == str2 返回 true ,而 str1 == str3 返回 false 。

          PS:毫不夸張地說,我的一個在阿里實習過的大佬同學,在面試某國企時就跪在了 equals 和 == 的區(qū)別上

          需要注意 substring()concat() 方法返回一個新的 String 對象,并將其保存在字符串池中。

          我們舉的例子都比較簡單,但如果我們考慮一些使用數(shù)百個字符串變量和數(shù)千個操作(如 substring() 或concat())的大型項目,僅使用 String 類可能會導致嚴重的內(nèi)存泄漏和時間延遲。

          這正是為什么還要設計 StringBuffer 或 StringBuilder 類的原因。

          StringBuffer & StringBuilder

          可變性(mutablity)

          StringBufferStringBuilder 對象與 String 對象一樣,三者都是字符序列。但是 StringBufferStringBuilder 是可變的(mutable),String 是不可變的(immutable),這意味著一旦我們?yōu)?StringBufferStringBuilder 初始化一個值,該值就會作為 StringBufferStringBuilder 對象的屬性進行處理。

          無論我們修改 StringBufferStringBuilder 的值多少次,都不會創(chuàng)建新的 String、StringBuffer 或StringBuilder 對象。StringBufferStringBuilder 的時間效率更高,資源消耗更少。

          StringBuilder vs StringBuffer

          這兩個類幾乎完全相同,它們使用同名的方法,返回相同的結果。但它們之間有兩個主要區(qū)別:

          • 線程安全

            StringBuffer 類是同步的(Synchronized),一次只能有一個線程調(diào)用 StringBuffer 實例的方法。StringBuilder 類是不同步的,多個線程可以調(diào)用 StringBuilder 類中的方法,而不會被阻塞。因此,StringBuffer 是線程安全的,而 StringBuffer 不是。如果我們開發(fā)的是多線程的應用程序,那么使用StringBuilder  可能會有風險。

          • 效率 StringBuffer 實際上比 StringBuilder 慢 2~3 倍。原因是 StringBuffer 線程安全(一次只允許一個線程在一個對象上執(zhí)行),會導致代碼執(zhí)行速度慢得多。

          方法

          StringBuilderStringBuffer 擁有相同的方法(除 StringBuffer 的方法都是 synchronized 的)。兩者常用的幾個方法:append() 、insert 、replace() 、delete() 、reverse() 。

          String vs StringBuilder vs StringBuffer


          StringStringBuilderStringBuffer
          可變性不可變可變可變
          線程安全
          時間效率較低
          內(nèi)存效率

          注意:從上表中看到的,String 類在時間和內(nèi)存上的效率都較低,但這并不意味著我們不應該再使用它。

          事實上,String 可以非常方便地使用,因為它可以快速編寫,我在實際開發(fā)經(jīng)常使用 public static final String 來定義字符串常量。

          String concatString = "concatString";
          StringBuffer appendBuffer = new StringBuffer("appendBuffer");
          StringBuilder appendBuilder = new StringBuilder("appendBuilder");
          long timerStarted;

          timerStarted = System.currentTimeMillis();
          for (int i = 0; i < 50000; i++) {
              concatString += " another string";
          }
          System.out.println("50000次String concat 操作的時間:" + (System.currentTimeMillis() - timerStarted) + "ms");

          timerStarted = System.currentTimeMillis();
          for (int i = 0; i < 50000; i++) {
              appendBuffer.append(" another string");
          }
          System.out.println("50000次 StringBuffer 的 append 操作時間:" + (System.currentTimeMillis() - timerStarted) + "ms");
                  
          timerStarted = System.currentTimeMillis();
          for (int i = 0; i < 50000; i++) {
              appendBuilder.append(" another string");
          }
          System.out.println("50000次 StringBuilder 的 append 操作時間:" + (System.currentTimeMillis() - timerStarted) + "ms");

          輸出:

          50000次String concat 操作的時間:18108ms
          50000次 StringBuffer 的 append 操作時間:7ms
          50000次 StringBuilder 的 append 操作時間:3ms

          根據(jù)您的Java虛擬機的不同,此輸出可能會有所不同。僅從這個測試結果可以看出,StringBuilder 在字符串操作方面是最快的。速度次快的是 StringBuffer ,它比 StringBuilder 慢 2~3 倍。Stringconcat 操作最慢。

          連接同樣多的字符串,StringBuilderString 快約 6000 倍,換句話說,StringBuilder 在 1 秒內(nèi)可以連接的字符串,String 需要 1.6 小時。

          總結

          可變性:String 是不可變類型,如果試圖更改 String 對象的值,則會在 String Pool 中創(chuàng)建另一個對象,而 StringBuffer 和StringBuilder 是可變的,其值可以更改。

          線程安全:StringBuffer 和 StringBuilder 之間的區(qū)別就在于 StringBuffer 是線程安全的。因此,當應用程序只需要在單線程中運行時,最好使用 StringBuilder ,因為 StringBuilder 比 StringBuffer 更高效。

          使用建議:

          • 如果程序中的字符串不會更改,比如 Java 中常量字符串的定義( public static final String ),建議使用 String 類,因為 String 對象本身就是 final 的。
          • 如果程序中字符串需要改變,且僅是單線程訪問,那么使用 StringBuilder 就足夠了。
          • 如果程序中字符串需要改變,且為多個線程訪問,建議使用 StringBuffer,因為 StringBuffer 是同步的,可以保證線程安全。


          國產(chǎn)小眾瀏覽器因屏蔽視頻廣告,被索賠100萬(后續(xù))

          年輕人“不講武德”:因看黃片上癮,把網(wǎng)站和786名女主播起訴了

          中國聯(lián)通官網(wǎng)被發(fā)現(xiàn)含木馬腳本,可向用戶推廣色情APP

          張一鳴:每個逆襲的年輕人,都具備的底層能力




          ,西,[]!


          瀏覽 45
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <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>
                  夜夜爽人妻 | 欧美精品一级 | 探花 的搜索结果 - 91n | 黄色一级片免费观看 | 豆花成人www、C0m |