1. 程式人生 > 其它 >String、StringBuffer、StringBuilder區別

String、StringBuffer、StringBuilder區別

最近我有了新的理解

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

    /** Cache the hash code for the string */
    private int hash; // Default to 0

String 類中使用 final 關鍵字修飾字符陣列來儲存字串,因此

String物件不可變,這句話不正確
我們知道被 final 關鍵字修飾的類不能被繼承,修飾的方法不能被重寫,修飾的變數是基本資料型別則值不能改變,修飾的變數是引用型別則不能再指向其他物件。因此,final 關鍵字修飾的陣列儲存字串並不是 String 不可變的根本原因,因為這個陣列儲存的字串是可變的。
如果引用為引用資料型別,比如物件、陣列,則該物件、陣列本身可以修改,但指向該物件或陣列的地址的引用不能修改。
String 真正不可變有下面幾點原因:

  • 儲存字串的陣列被 final 修飾且為私有的,並且String 類沒有提供/暴露修改這個字串的方法.
  • String 類被 final 修飾導致其不能被繼承,進而避免了子類破壞 String 不可變.
  • StringBuilder/StringBuffer
abstract class AbstractStringBuilder implements Appendable, CharSequence {
    /**
     * The value is used for character storage.
     */
    char[] value;


兩者繼承父類AbstractStringBuilder ,底層也是字元陣列,當時沒有private和final修飾,而且提供很多修改字元陣列的方法

  • 執行緒安全性:String不可變,常量,安全,StringBuilder/StringBuffer都繼承AbstractStringBuilder ,存在很多公共方法.但是StringBuffer 有同步鎖,執行緒安全.反之,StringBuilder沒有同步鎖,不安全.
  • 效能:每次改變String的時候,都會新建String物件,浪費記憶體,降低效能.StringBuffer 有鎖機制,原地改變,還是會浪費效能.StringBuilder不存在鎖機制,原地改變,不考慮安全前提,效能好.
    總結:大資料,多執行緒,安全->StringBuffer ;大資料,單執行緒->StringBuilder;小資料,安全->String
   String str1 = "he";
        String str2 = "llo";
        String str3 = "world";
        String str4 = str1 + str2 + str3;
// class version 52.0 (52)
// access flags 0x21
public class practice/begin/Code25_Demo {

  // compiled from: Code25_Demo.java

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 8 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lpractice/begin/Code25_Demo; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x9
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 10 L0
    LDC "he"
    ASTORE 1
   L1
    LINENUMBER 11 L1
    LDC "llo"
    ASTORE 2
   L2
    LINENUMBER 12 L2
    LDC "world"
    ASTORE 3
   L3
    LINENUMBER 13 L3
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    ALOAD 1
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 3
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    ASTORE 4
   L4
    LINENUMBER 15 L4
    RETURN
   L5
    LOCALVARIABLE args [Ljava/lang/String; L0 L5 0
    LOCALVARIABLE str1 Ljava/lang/String; L1 L5 1
    LOCALVARIABLE str2 Ljava/lang/String; L2 L5 2
    LOCALVARIABLE str3 Ljava/lang/String; L3 L5 3
    LOCALVARIABLE str4 Ljava/lang/String; L4 L5 4
    MAXSTACK = 2
    MAXLOCALS = 5
}

物件引用和“+”的字串拼接方式,實際上是通過 StringBuilder 呼叫 append() 方法實現的,拼接完成之後呼叫 toString() 得到一個 String 物件 。

  String[] arr = {"he", "llo", "world"};
        String s = "";
        for (int i = 0; i < arr.length; i++) {
            s += arr[i];
        }
        System.out.println(s);
// class version 52.0 (52)
// access flags 0x21
public class practice/begin/Code25_Demo {

  // compiled from: Code25_Demo.java

  // access flags 0x1
  public <init>()V
   L0
    LINENUMBER 8 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lpractice/begin/Code25_Demo; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x9
  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 14 L0
    ICONST_3
    ANEWARRAY java/lang/String
    DUP
    ICONST_0
    LDC "he"
    AASTORE
    DUP
    ICONST_1
    LDC "llo"
    AASTORE
    DUP
    ICONST_2
    LDC "world"
    AASTORE
    ASTORE 1
   L1
    LINENUMBER 15 L1
    LDC ""
    ASTORE 2
   L2
    LINENUMBER 16 L2
    ICONST_0
    ISTORE 3
   L3
   FRAME APPEND [[Ljava/lang/String; java/lang/String I]
    ILOAD 3
    ALOAD 1
    ARRAYLENGTH
    IF_ICMPGE L4
   L5
    LINENUMBER 17 L5
    NEW java/lang/StringBuilder
    DUP
    INVOKESPECIAL java/lang/StringBuilder.<init> ()V
    ALOAD 2
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    ALOAD 1
    ILOAD 3
    AALOAD
    INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;
    INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;
    ASTORE 2
   L6
    LINENUMBER 16 L6
    IINC 3 1
    GOTO L3
   L4
    LINENUMBER 19 L4
   FRAME CHOP 1
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ALOAD 2
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L7
    LINENUMBER 22 L7
    RETURN
   L8
    LOCALVARIABLE i I L3 L4 3
    LOCALVARIABLE args [Ljava/lang/String; L0 L8 0
    LOCALVARIABLE arr [Ljava/lang/String; L1 L8 1
    LOCALVARIABLE s Ljava/lang/String; L2 L8 2
    MAXSTACK = 4
    MAXLOCALS = 4
}

編譯器不會建立單個 StringBuilder 以複用,會導致建立過多的 StringBuilder 物件。浪費空間.因此,在迴圈中進行字串拼接,直接使用StringBuilder比較好.