首頁  >  文章  >  後端開發  >  String StringBuffer StringBuilder 區別

String StringBuffer StringBuilder 區別

巴扎黑
巴扎黑原創
2016-12-20 15:49:411306瀏覽

String 字串常數 
StringBuffer 字串變數(線程安全) 
StringBuilder 字串變數(非線程安全) 
簡要的說, String 類型和StringBuffer 類型的主要效能區別其實在於String 是不可變的物件, 因此在每次對String 類型改變的時候其實都等於產生了一個新的String 對象,然後將指標指向新的String 對象,所以經常改變內容的字串最好不要用String ,因為每次產生物件都會對系統效能產生影響,特別當記憶體中無引用物件多了以後, JVM 的GC 就會開始運作,那速度是一定會相當慢的。 
而如果是使用 StringBuffer 類別則結果就不一樣了,每次結果都會對 StringBuffer 物件本身進行操作,而不是產生新的對象,再改變物件參考。所以在一般情況下我們推薦使用 StringBuffer ,特別是字串物件經常改變的情況下。而在某些特別情況下, String 物件的字串拼接其實是被JVM 解釋成了StringBuffer 物件的拼接,所以這些時候String 物件的速度不會比StringBuffer 物件慢,而特別是以下的字串物件生成中, String 效率是遠要比StringBuffer 快的: 
String S1 = “This is only a” + “ simple” + “ test”; 
StringBuffer Sb = new StringBuilder(“This is only a”).append(“ simple ”).append(“ test”); 
你會很驚訝的發現,生成String S1 物件的速度簡直太快了,而這個時候StringBuffer 居然速度上根本一點都不佔優勢。其實這是JVM 的把戲,在JVM 眼裡,這個 
String S1 = “This is only a” + “ simple” + “test”; 其實是: 
String S1 = “This is only a simple test”; 所以當然不需要太多的時間了。但大家這裡要注意的是,如果你的字串是來自另一個的String 物件的話,速度就沒那麼快了,譬如: 
String S2 = “This is only a”; 
String S3 = “ simple”; 
String S4 = “ test”; 
String S1 = S2 +S3 + S4; 
這時候JVM 會規規矩矩的按照原來的方式去做 

在大部分情況下StringBuffer > String 
StringBuffer 
.線程安全的可變字元序列。一個類似 String 的字串緩衝區,但不能修改。雖然在任意時間點上它都包含某種特定的字元序列,但透過某些方法呼叫可以改變該序列的長度和內容。 
可將字串緩衝區安全地用於多個執行緒。可以在必要時對這些方法進行同步,因此任意特定實例上的所有操作就好像是以串行順序發生的,該順序與所涉及的每個線程進行的方法調用順序一致。 
StringBuffer 上的主要操作是 append 和 insert 方法,可重載這些方法,以接受任意類型的資料。每個方法都能有效地將給定的資料轉換成字串,然後將該字串的字元追加或插入到字串緩衝區中。 append 方法始終將這些字元新增至緩衝區的末端;而 insert 方法則在指定的點新增字元。
例如,如果z 引用一個當前內容是“start”的字串緩衝區對象,則此方法調用z.append("le") 會使字串緩衝區包含“startle”,而z.insert(4, "le") 將更改字串緩衝區,使其包含「starlet」。 
在大部分情況下 StringBuilder > StringBuffer 
java.lang.StringBuilde 
java.lang.StringBuilder一個可變的字元序列是5.0新增的。此類別提供一個與 StringBuffer 相容的 API,但不保證同步。該類別被設計用作 StringBuffer 的一個簡易替換,用在字串緩衝區被單一執行緒使用的時候(這種情況很普遍)。如果可能,建議優先採用該類,因為在大多數實作中,它比 StringBuffer 更快。兩者的方法基本上相同。
================================================= ======================== 
String類別詳解 
1、String類別是final的,不可被繼承。 
2、String類別是的本質是字元陣列char[], 且其值無法改變。 PRivate final char value[]; 
接著開啟String類別的API文檔,可以發現: 
3、String類別物件有個特殊的建立的方式,就是直接指定例如String x = "abc","abc"就表示一個字串物件。而x是"abc"物件的地址,也叫 

做"abc"物件的引用。 
4、String物件可以透過「+」串連。串聯後會產生新的字串。也可以透過concat()來串聯,這個後面會講述。 
6、Java執行時會維護一個String Pool(String池),JavaDoc翻譯很模糊「字串緩衝區」。 String池用來存放運作時產生的各種字串, 

且池中的字串的內容不重複。而一般物件不存在這個緩衝池,並且所建立的物件僅僅存在於方法的堆疊區。 

其一,使用new關鍵字建立字串,例如String s1 = new String("abc"); 
其二,直接指定。例如String s2 = "abc"; 
其三,使用串連產生新的字串。例如String s3 = "ab" + "c"; 

二、String物件的建立 

String物件的建立也非常講究,關鍵在於明白其原理。
原理1:當使用任何方式來建立一個字串物件s時,Java運行時(運行中JVM)會拿著這個X在String池中找是否存在內容相同的字串對象, 

如果不存在,則在池中建立一個字串s,否則,不在池中新增。 

原理2:Java中,只要使用new關鍵字來建立對象,則一定會(在堆疊區或堆疊區)建立一個新的物件。 

原理3:使用直接指定或使用純字串串聯來建立String對象,則只會檢查維護String池中的字串,池中沒有就在池中建立一個,有則罷 

了!但絕不會在堆疊區再去建立該String物件。 

原理4:使用包含變數的表達式建立String對象,則不僅會檢查維護String池,還會在堆疊區建立一個String物件。 

另外,String的intern()方法是一個本地方法,定義為public native String intern(); intern()方法的價值在於讓開發者能將注意力集中到 

String池上。當呼叫 intern 方法時,如果池已經包含一個等於此 String 物件的字串(該物件由 equals(Object) 方法決定),則傳回池 

中的字串。否則,將此 String 物件新增至池中,並且傳回此 String 物件的參考。 

三、不可變類 
不可改變的字串有一個很大的優點:編譯器可以把字串設定為共享。 
不可變類別String有一個重要的優點-它們不會被分享引用。

是這樣的,JAVA為了提高效率,所以對String類型進行了特別的處理---為string型別提供了串列池 
定義一個string型別的變數有兩種方式: 
string name= "tom ";
string name =new string( "tom ") 
使用第一種方式的時候,就使用了串池, 
使用第二中方式的時候,就是一種普通的聲明對象的方式 
如果你使用了第一種方式,那麼當你在聲明一個內容也是"tom "的string時,它將使用串池裡原來的那個內存,而不會重新分配內存,也就是說,string saname= "tom ",將會指向同一塊記憶體 

另外關於string型別是不可改變的問題: 
string型別是不可改變的,也就是說,當你想改變一個string物件的時候,例如name= "madding " 
那麼虛擬機不會改變原來的對象,而是產生一個新的string對象,然後讓name去指向它,如果原來的那個"tom "沒有任何對象去引用它,虛擬機的垃圾回收機制將接收它。 
據說這樣可以提高效率! ! !
================================================= ========================final StringBuffer a = new StringBuffer("111");  
final StringBuffer b = new StringBuffer("222");  
a=b;//此句編譯不透過  
final StringBuffer a = new StringBuffer("111");  
a.append("222");//編譯透過   
可見,而final只對引用的"值"(即記憶體位址)有效,它迫使引用只能指向初始指向的那個對象,改變它的指向會導致編譯期錯誤。至於它所指向的對象的​​變化,final是不負責的。

String常數池問題的四個例子 
以下是幾個常見例子的比較分析與理解: 

[1]  
String a = "a1";   
String b = "a" + 1; .println((a == b)); //result = true 
String a = "atrue";   
String b = "a" + "true";   
System.out.println((a == b))) ; //result = true 
String a = "a3.4";   
String b = "a" + 3.4;   
System.out.println((a == b)); //result = true  
分析:JVM對於字串常數的"+"號連接,將程式編譯期,JVM就將常數字串的"+"連接最佳化為連接後的值,拿"a" + 1來說,經編譯器最佳化後在class中就已經是a1。在編譯期其字串常數的值就確定下來,故上面程式最終的結果都為true。

[2]  
String a = "ab";   
String bb = "b";   
String b = "a" + bb;   
System.out.print b = "a" + bb;   false  
分析:JVM對於字串引用,由於在字串的"+"連接中,有字串引用存在,而引用的值在程式編譯期是無法確定的,即"a" + bb無法被編譯器最佳化,只有在程式運行期來動態分配並將連接後的新位址賦給b。所以上面程式的結果也就為false。

[3] 
String a = "ab";   
final String bb = "b";  
String b = "a" + bb;   
System.out.println((a ult)); = true  
分析:和[3]中唯一不同的是bb字串加了final修飾,對於final修飾的變量,它在編譯時被解析為常數值的一個本地拷貝存儲到自己的常數池中或嵌入到它的字節碼流中。 
所以此時的"a" + bb和"a" + "b"效果是一樣的。故上面程式的結果為true。

[4]  
String a = "ab";   
final String bb = getBB();   
String b = "a" + bb;   
System
String b = "a" + bb;   
System。 = false   
private static String getBB() {  return "b";   }   
分析:JVM對於字串引用bb,它的值在編譯期間無法確定,只有在程式執行期呼叫方法後,將方法的傳回值與字串和"a"來動態連線並指派位址為b,故上面程式的結果為false。

透過上面4個例子可以得知: 

String  s  =  "a" + "b" + "c";     就等價於String s = "abc" 

String
String  b  =  "b";     
String  c  =  "c";     
String  s  =   a uffer temp = new StringBuffer();     
temp. append(a).append(b).append(c);     
String s = temp.toString();   
由上述分析的結果,因此不難推論出String 採用連結運算子(+)效率低原因分析,形如這樣的程式碼: 

public class Test {  
public static void main(String args[]) {  
String s = null;  
for(int i = 0; i }  
}   
每做一次+ 就產生個StringBuilder對象,而append後就丟掉。下次循環再到達時重新產生個StringBuilder對象,然後 append 字串,如此循環直至結束。 如果我們直接採用 StringBuilder 物件進行 append 的話,我們可以節省 N - 1 次建立和銷毀物件的時間。所以對於在迴圈中要進行字串連接的應用,一般都是用StringBuffer或StringBulider物件來進行append操作。

String物件的intern方法理解與分析 
public class Test4 {  
private static String a = "ab";   
public static void main(String[] args){ 
String s1 = "; b"; 
String s = s1 + s2;  
System.out.println(s == a);//false  
System.out.println(s.intern() == a);//true  }   
這裡用到Java裡面是一個常數池的問題。對於s1+s2操作,其實是在堆裡面重新創建了一個新的物件,s保存的是這個新物件在堆空間的的內容,所以s與a的值是不相等的。而當呼叫s.intern()方法,卻可以傳回s在常數池中的位址值,因為a的值儲存在常數池中,故s.intern和a的值相等。

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn