String、StringBuffer、StringBuilder區別
阿新 • • 發佈:2022-02-12
最近我有了新的理解
- 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比較好.