StringBuffer 和 String

當我們進行字符拼接時,請使用StringBuffer類而非String類,因爲前者將比後者快上百倍。

String str="You are nice.";
str+="I love you so much.";

如果用StringBuffer類的話,代碼如下:
StringBuffer str= new StringBuffer("You are nice.");
str.append("I love you so much.");

用法:

StringBuffer sb = new StringBuffer(); sb.append("yacht1") ; sb.append("yacht2"); sb.append("yacht3") ; sb.append("yacht4");     String a = sb.toString();

String && StringBuffer的區別:
非可變對象一旦創建之後就不能再被改變,可變對象則可以在創建之後被改變。String對象是非可變對象;StringBuffer對象則是可變對象。爲獲得更佳的性能需要根據實際情況小心謹慎地選擇到底使用這兩者中的某一個。
String類用來表示那些創建後就不會再改變的字符串,它是不可變的(immutable);
StringBuffer類用來表示內容可變的字符串;
例:
1.String對象:
String str = "Hello";
str += "World";
//     JVM會創建一個臨時的StringBuffer類對象,並調用其append()方法完成字符串的拼接,這是因爲 String類是不可變的,拼接操作不得不使用StringBuffer類(並且--JVM會將"Hello"和"World"創建爲兩個新的 String對象)。之後,再將這個臨時StringBuffer對象轉型爲一個String,代價不菲!可見,在這一個簡單的一次拼接過程中,我們讓程 序創建了四個對象:兩個待拼接的String,一個臨時StringBuffer,和最後將StringBuffer轉型成爲 的String--它不是最初的str,而是最初的str的引用指向了新生成的String對象"HelloWorld"。
2.StringBuffer對象:
StringBuffer strBuf = new StringBuffer("Hello");
strBuf.append("World");
//     程序將只產生兩個對象:最初的strBuf :"Hello"和拼接時的String("World"),不再需要創建臨時的StringBuffer類對象而後還得將其轉換回String對象。節省額外的系統開銷。

如何選擇是使用String還是StringBuffer:
取決於兩種情況,第一種情況是需要連接的字符串是在編譯期決定的還是在運行期決定的,第二種情況是你使用的是StringBuffer還是String。
1) 第一種情況:編譯期決定相對於運行期決定;如:
String str = "This " + "is " + "a " + "Java " + "program";
StringBuffer strBuf = new StringBuffer();
strBuf.append("This ");
strBuf.append("is ");
strBuf.append("a ");
strBuf.append("Java ");
strBuf.append("program");

此時,+操作符比StringBuffer.append()方法要快,WHY?這裏編譯器的優化起了關鍵作用,編譯器簡單地在編譯期連接多個字符串。 它使用編譯期決定取代運行期決定,在你使用new關鍵字來創建String對象的時候也是如此。這裏str對象在編譯期就決定了而 StringBuffer對象是在運行期決定的。運行期決定需要額外的開銷當字符串的值無法預先知道的時候,編譯期決定作用於字符串的值可以預先知道的時 候,也就是說String str = "This " + "is " + "a " + "Java " + "program";這段代碼在編譯時,編譯器會對程序作出優化,String str被優化成“This is a Java program”;而StringBuffer strBuf只會在運行時才處理。所以效率是不一樣的。(注意,這裏的String str = "This " + "is " + "a " + "Java " + "program";與 String str = "Hello";
str += "World";是不一樣的);
2) 第二種情況:使用StringBuffer取代String
String str = "Hello";
for(int i = 0; i < 40000; i++) {
     str += "World";
}

StringBuffer strBuf = new StringBuffer("Hello");
for(int i = 0; i < 40000; i++) {
      strBuf.append("World");
}
此時,StringBuffer.append()方法要比+操作符快得多,WHY?原因是兩者都是在運行期決定字符串對 象,但是+操作符使用不同於StringBuffer.append()的規則;它是通過String和StringBuffer來完成字符串的連接操作 的。

另外,在使用StringBuffer時,可以通過StringBuffer的構造函數來設定它的初始化容量,這樣可以明顯地提升性能。這裏提到的構造函 數是StringBuffer(int length),length參數表示當前的StringBuffer能保持的字符數量。如:
(1)StringBuffer strBuf = new StringBuffer();
             for(int i = 0; i < 40000; i++) {
                  strBuf.append("Hello");
             }
(2)
StringBuffer strBuf = new StringBuffer(100000);
             for(int i = 0; i < 40000; i++) {
                  strBuf.append("Hello");
             }
此時,(2) 的效率好於 (1),因爲StringBuffer內部實現是char數組,當使用缺省的構造函數來創建StringBuffer對象的時候,因爲沒有設置初始化字符 長度,StringBuffer的容量被初始化爲16個字符,也就是說缺省容量就是16個字符。當StringBuffer達到最大容量的時候,它會將自 身容量增加到當前的2倍再加2,也就是(2*舊值+2)。 如果使用缺省值,初始化之後接着往裏面追加字符,在追加到第16個字符的時候它會將容量增加到 34(2*16+2),當追加到34個字符的時候就會將容量增加到70(2*34+2)。無論何事只要StringBuffer到達它的最大容量它就不得 不創建一個新的字符數組然後重新將舊字符和新字符都拷貝一遍。所以總是給StringBuffer設置一個合理的初始化容量值是錯不了的,這樣會帶來立竿 見影的性能增益。(2)避免了複製數組的開銷。

創建String對象:
String str = "Hello";
//  
JVM先根據內容"Hello"查找對象,如果沒找到,則在heap(堆棧)*上創建新對象,並將其賦予str,否則使用已經存在的對象。
String str = new String("Hello");
//   
據內不管heap上有沒有"Hello"這個對象,JVM都會在heap上創建一個新String對象;此時heap上可能會出現內容相同,地址不同的String對象。
推薦使用String str = "Hello";這種方法創建String類型的對象,這樣會使heap中只存在唯一的一個存放"Hello"對象的內存地址;實際上我們不需要多個獨立的"Hello"對象,因爲要運行它們的話浪費時間+浪費內存。我們也不必因使用new String("Hello");創 建了多個”Hello”對象而發愁,可以使用intern()方法來避免在堆內存上創建重複的String對象來改善Java的運行性能。String intern()方法檢查字符串對象的存在性,如果需要的字符串已經存在,那麼它將會引用指向已經存在的字符串對象而不是重新創建一個。這和使用 String str = "Hello";這種方法創建對象就作用上來說是一致的。使用intern():
String str = new String("Hello");
str = str.intern();

String對象的比較:
"=="            //比較地址;
"equals"    //比較內容;
String str1 = "a";
String str2 = "a";
String str3 = new String("a");
str1 == str2        //    true
str1 == str3        //    false
str1.equals(str2);       //   true
str1.equals(str3);       //   true

但是StringBuffer類並沒有實現Objcet類的Equals方法,所以不能用這個方法來比較兩個StringBuffer類的字符串是否相等:
StringBuffer strBuf1 = new StringBuffer(“a”);
StringBuffer strBuf2 = new StringBuffer(“a”);
     System.out.println(
strBuf1.equals(strBuf2));
程序輸出:false

*這裏想對“heap(堆棧)”做一下更正:應該是heap(堆),而非堆棧。
“堆”是一種通用的內存池,用於存放所有的Java對象。當使用“new”實例化一個對象時,編譯器會自動在堆裏進行存儲分配。
C++在堆棧中創建對象,Java對象引用存儲在堆棧中;而Java對象並不存儲於其中。
堆棧:堆棧指針向下移動,分配新內存,向上移動,釋放內存;創建程序時編譯器必須知道存儲在堆棧中所有數據的確切大小和生命週期,因爲要通過代碼實現來控制上下移動堆棧指針,這一約束限制了程序的靈活性。而如果是在堆上創建對象,編譯器不需要知道從堆裏分配多少存儲區域,也不必知道存儲的數據在堆裏存活多長時間。因此,在堆裏分配存儲有很大的靈活性。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章