1. 程式人生 > >String、StringBuilder和StringBuffer詳解

String、StringBuilder和StringBuffer詳解

以JDK1.8原始碼為例

一、原始碼

String:

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    ……
}

StringBuilder:

public final class StringBuilder
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    ……
    @Override
    public StringBuilder append(String str) {
        super.append(str);
        return this;
    }
}

StringBuffer:

 public final class StringBuffer
    extends AbstractStringBuilder
    implements java.io.Serializable, CharSequence
{
    ……
    @Override
    public synchronized StringBuffer append(String str) {
        toStringCache = null;
        super.append(str);
        return this;
    }

}

從原始碼中我們能看到:

1、三者都使用了final修飾,不能被繼承;

2、三者都實現了java.io.Serializable介面,均可序列化和反序列化;

3、三者均實現同一介面CharSequence;

4、StringBuffer和StringBuilder都繼承自同一個抽象類AbstractStringBuilder;

5、StringBuffer大部分方法前使用了synchronized關鍵字,而StringBuilder並沒有使用。

 

二、String

1、String是不可變物件

首先,String是類,並不屬於基本資料型別,雖然在使用中,有些地方和基本資料型別相似,例如:

String str="a";
int i=0;

String可以像基本資料型別一樣賦值。但是它本質上是類,例如:

String s= new String("a");

其次,字串一旦建立,物件永遠無法改變,但字串引用可以重新賦值,例如:

String str = "a";
str = str + "b";

程式碼中字串"a"一旦建立用法無法改變,但是字串引用str可以重新賦值為str+"b",也就是字串"ab",事實上,在這個過程中總共建立了三個字串物件,分別是:"a","b","ab"。

2、String常量池

java為提高字串使用效能,靜態字串(字面量/常量/常量連線的結果)在字串常量池中建立,並儘量使用同一個物件,重用靜態字串,對重複出現的字串直接量,JVM會首先在常量池中查詢,如果存在即返回該物件。例如:

//在字串常量池中建立了"hello"物件,然後str1引用該物件
String str1="hello";
//不會建立新的"hello"物件,直接使用常量池中已有的"hello"物件
String str2="hello";
//字串連線結果也是"hello",不會再建立"hello",直接使用常量池中"hello"
//但是注意的是,會建立"he"和"llo"物件
String str3="he"+"llo";
//輸出true
System.out.println(str1==str2);
//輸出true
System.out.println(str2==str3);

結果輸出結果均為true,也就是str1、str2和str3都是相等的。

3、String記憶體編碼及長度

Strign在記憶體中採用Unicode編碼,任何一個字元(無論是中文還是英文)都算一個字元長度,佔用兩個位元組。例如:

String str1="he";
String str2="你好";
//輸出2
System.out.println(str1.length());
//輸出2
System.out.println(str2.length());

 

三、StringBuilder和StringBuffer

1、StringBuilder和StringBuffer是可變物件

StringBuilder builder = new StringBuilder("a");
//輸出a
System.out.println(builder);
builder.append("b");
//輸出ab
System.out.println(builder);

2、StringBuilder的方法返回值

StringBuilder的很多方法的返回值也是StringBuilder,所以可以對一些操作連用,例如:

StringBuilder builder = new StringBuilder();
builder.append("a").append(1).append('c').deleteCharAt(2);
//輸出a1
System.out.println(builder);

如果對字串有大量的計算,應當選擇StringBuilder,String在字串運算中速度較慢。

3、StringBuffer與StringBuilder

兩者基本相同,主要線上程操作中不同。StringBuffer因為使用了synchronized關鍵字,是執行緒安全的,而StringBuilder未使用synchronized關鍵字,是非執行緒安全的。例如:

package com.leboop;

public class StringTest {
	public static void main(String[] args) {
        StringBuilder builder = new StringBuilder();
        StringBuffer buffer = new StringBuffer();
        //迴圈建立1000個執行緒
        for(int i=0;i<1000;i++){
        	new Thread(new Runnable() {
				@Override
				public void run() {
					// TODO Auto-generated method stub
					//每個執行緒都要追加100個1
					for(int j=0;j<100;j++){
						builder.append("1");
						buffer.append("1");
						System.out.println(builder.length()+"-"+buffer.length());
					}
				}
			}).start();
        }
	}	
}

輸出結果

99988-99992
99989-99993
99990-99994
99991-99995
99992-99996
99993-99997
99994-99998
99995-99999
99996-100000

正確結果應該是100000。顯然StringBuilder非執行緒安全,如果要深入理解需要理解併發程式設計和jvm記憶體模型。所以在實際應用中,少量的單執行緒字串操作使用String,大量的單執行緒字串操作使用StringBuilder,多執行緒字串操作使用StringBuffer。