java中String、StringBuffer、StringBuilder是編程中經(jīng)常使用的字符串類,在上一篇博文中我們已經(jīng)熟悉String字符串的特性和使用,而StringBuffer、StringBuilder又是怎么樣的字符串類呢??他們之間的區(qū)別和關(guān)系又是什么呢??這問題經(jīng)常在面試中會問到,現(xiàn)在總結(jié)一下,看看他們的不同與相同。
1.可變與不可變
1)String類中使用字符數(shù)組保存字符串,如下就是,因為有“final”修飾符,所以可以知道string對象是不可變的。
private final char value[];
String的值是不可變的,這就導(dǎo)致每次對String的操作都會生成新的String對象,不僅效率低下,而且大量浪費有限的內(nèi)存空間。
1 String a = "a"; //假設(shè)a指向地址0x00012 a = "b";//重新賦值后a指向地址0x0002,但0x0001地址中保存的"a"依舊存在,但已經(jīng)不再是a所指向的,a 已經(jīng)指向了其它地址。
因此String的操作都是改變賦值地址而不是改變值操作。
2)StringBuilder與StringBuffer都繼承自AbstractStringBuilder類,在AbstractStringBuilder中也是使用字符數(shù)組保存字符串,如下就是,可知這兩種對象都是可變的。
char[] value;
StringBuffer是可變類,和線程安全的字符串操作類,任何對它指向的字符串的操作都不會產(chǎn)生新的對象。 每個StringBuffer對象都有一定的緩沖區(qū)容量,當(dāng)字符串大小沒有超過容量時,不會分配新的容量,當(dāng)字符串大小超過容量時,會自動增加容量。
1 StringBuffer buf=new StringBuffer(); //分配長16字節(jié)的字符緩沖區(qū)2 StringBuffer buf=new StringBuffer(512); //分配長512字節(jié)的字符緩沖區(qū)3 StringBuffer buf=new StringBuffer("this is a test")//在緩沖區(qū)中存放了字符串,并在后面預(yù)留了16字節(jié)的空緩沖區(qū)。
StringBuffer和StringBuilder類功能基本相似,主要區(qū)別在于StringBuffer類的方法是多線程、安全的,而StringBuilder不是線程安全的,相比而言,StringBuilder類會略微快一點。對于經(jīng)常要改變值的字符串應(yīng)該使用StringBuffer和StringBuilder類。
2.是否多線程安全
String中的對象是不可變的,也就可以理解為常量,顯然線程安全。
AbstractStringBuilder是StringBuilder與StringBuffer的公共父類,定義了一些字符串的基本操作,如expandCapacity、append、insert、indexOf等公共方法。
StringBuffer對方法加了同步鎖或者對調(diào)用的方法加了同步鎖,所以是線程安全的。看如下源碼:
1 public synchronized StringBuffer reverse() {2 super.reverse();3 return this;4 }5 6 public int indexOf(String str) {7 return indexOf(str, 0); //存在 public synchronized int indexOf(String str, int fromIndex) 方法8 }
StringBuilder并沒有對方法進行加同步鎖,所以是非線程安全的。
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
StringBuffer是線程安全的,這意味著它們已經(jīng)同步方法來控制訪問,以便只有一個線程可以在同一時間訪問一個StringBuffer對象同步代碼。因此,StringBuffer的對象通常在多線程環(huán)境中是安全的,使用多個線程可以試圖同時訪問相同StringBuffer對象。
StringBuilder類非常相似的StringBuffer,不同之處在于它的訪問不同步的,因此,它不是線程安全的。由于不同步,StringBuilder的性能可以比StringBuffer更好。因此,如果在單線程環(huán)境中工作,使用StringBuilder,而不是StringBuffer可能會有更高的性能。這也類似其他情況,如StringBuilder的局部變量(即一個方法中的一個變量),其中只有一個線程會訪問一個StringBuilder對象。
3.StringBuffer和StringBuilder類的速度比較
一般情況下,速度從快到慢:StringBuilder>StringBuffer>String,這種比較是相對的,不是絕對的。(要考慮程序是單線程還是多線程)
接下來,我直接貼上測試過程和結(jié)果的代碼,一目了然:
1 package com.hysum.test; 2 3 public class StringTest { 4 final static int time = 50000; //循環(huán)次數(shù) 5 /* 6 * String類測試方法 7 */ 8 public void test(String s){ 9 long begin = System.currentTimeMillis();//獲取當(dāng)前系統(tǒng)時間(毫秒數(shù)),開始10 for(int i=0; i<time; i++){11 s += "add";12 }13 long over = System.currentTimeMillis();//獲取當(dāng)前系統(tǒng)時間(毫秒數(shù)),結(jié)束14 System.out.println("操作"+s.getClass().getName()+"類型使用的時間為:"+(over-begin)+"毫秒");15 }16 /*17 * StringBuffer類測試方法18 */19 public void test(StringBuffer s){20 long begin = System.currentTimeMillis();21 for(int i=0; i<time; i++){22 s.append("add");23 }24 long over = System.currentTimeMillis();25 System.out.println("操作"+s.getClass().getCanonicalName()+"類型使用的時間為:"+(over-begin)+"毫秒");26 }27 /*28 * StringBuilder類測試方法29 */30 public void test(StringBuilder s){31 long begin = System.currentTimeMillis();32 for(int i=0; i<time; i++){33 s.append("add");34 }35 long over = System.currentTimeMillis();36 System.out.println("操作"+s.getClass().getName()+"類型使用的時間為:"+(over-begin)+"毫秒");37 }38 39 /*對 String 直接進行字符串拼接的測試*/40 public void test2(){//操作字符串對象引用相加類型使用的時間41 String s2 = "abcd";42 long begin = System.currentTimeMillis();43 for(int i=0; i<time; i++){44 String s = s2 + s2 +s2;45 }46 long over = System.currentTimeMillis();47 System.out.println("操作字符串對象引用相加類型使用的時間為:"+(over-begin)+"毫秒");48 }49 public void test3(){//操作字符串相加使用的時間50 long begin = System.currentTimeMillis();51 for(int i=0; i<time; i++){52 String s = "abcd" + "abcd" + "abcd";53 }54 long over = System.currentTimeMillis();55 System.out.println("操作字符串相加使用的時間為:"+(over-begin)+"毫秒");56 } 57 public static void main(String[] args) {58 // TODO Auto-generated method stub59 String s1 = "abcd";60 StringBuffer st1 = new StringBuffer( "abcd");61 StringBuilder st2 = new StringBuilder( "abcd");62 StringTest tc = new StringTest();63 tc.test(s1);64 tc.test(st1);65 tc.test(st2);66 tc.test2();67 tc.test3(); 68 }69 70 }
運行結(jié)果:
結(jié)果分析:
從上面的結(jié)果可以看出,不考慮多線程,采用String對象時,執(zhí)行時間比其他兩個都要高得多,而采用StringBuffer對象和采用StringBuilder對象的差別也比較明顯;而以String類為例,操作字符串對象引用相加類型使用的時間比直接/操作字符串相加使用的時間也多得多。由此可見,如果我們的程序是在單線程下運行,或者是不必考慮到線程同步問題,我們應(yīng)該優(yōu)先使用StringBuilder類;如果要保證線程安全,自然是StringBuffer;能直接操作字符串不用字符串引用就直接操作字符串。
4、StringBuilder與StringBuffer共同點
StringBuilder與StringBuffer有公共父類AbstractStringBuilder(抽象類)。
StringBuilder、StringBuffer的方法都會調(diào)用AbstractStringBuilder中的公共方法,如super.append(...)。只是StringBuffer會在方法上加synchronized關(guān)鍵字,進行同步。
那么我們接來下看一下它們的主要方法吧~
方法 | 說明 |
StringBuffer append(參數(shù)) | 追加內(nèi)容到當(dāng)前StringBuffer對象的末尾,類似于字符串的連接 |
StringBuffer deleteCharAt(int index) | 刪除指定位置的字符,然后將剩余的內(nèi)容形成新的字符串 |
StringBuffer insert(位置, 參數(shù)) | 在StringBuffer對象中插入內(nèi)容,然后形成新的字符串 |
StringBuffer reverse() | 將StringBuffer對象中的內(nèi)容反轉(zhuǎn),然后形成新的字符串 |
void setCharAt(int index, char ch) | 修改對象中索引值為index位置的字符為新的字符ch |
void trimToSize() | 將StringBuffer對象的中存儲空間縮小到和字符串長度一樣的長度,減少空間的浪費,和String的trim()是一樣的作用 |
StringBuffer delete(int start, int end) | 刪除指定區(qū)域的字符串 |
StringBuffer replace(int start, int end, String s) | 用新的字符串替換指定區(qū)域的字符串 |
void setlength(int n) | 設(shè)置字符串緩沖區(qū)大小 |
int capacity() | 獲取字符串的容量 |
void ensureCapacity(int n) | 確保容量至少等于指定的最小值。如果當(dāng)前容量小于該參數(shù),然后分配一個新的內(nèi)部數(shù)組容量更大。新的容量是較大的. |
getChars(int start,int end,char chars[],int charStart); | 將字符串的子字符串復(fù)制給數(shù)組 |
以下是各個方法的代碼示例:
1 public static void main(String[] args) { 2 // TODO Auto-generated method stub 3 StringBuilder str=new StringBuilder("學(xué)習(xí) java 編程"); 4 5 //增加字符串內(nèi)容的方法 6 //append(參數(shù)),追加內(nèi)容到當(dāng)前對象的末尾 7 str.append("學(xué)習(xí)使我快樂"); 8 System.out.println("追加內(nèi)容到當(dāng)前對象的末尾:"+str); 9 // insert(位置, 參數(shù)),在對象中插入內(nèi)容10 str.insert(10,',');11 System.out.println("在對象中插入內(nèi)容:"+str);12 13 //操作字符串內(nèi)容的方法14 //delete(int start, int end),刪除指定區(qū)域的字符串15 str.delete(11, 17);16 System.out.println("刪除指定區(qū)域的字符串:"+str);17 //deleteCharAt(int index),刪除指定位置的字符18 str.deleteCharAt(10);19 System.out.println("刪除指定位置的字符:"+str);20 //setCharAt(int index, char newChar),修改對象中索引值為index位置的字符為新的字符ch21 str.setCharAt(3, 'J');22 System.out.println("修改對象中索引值為index位置的字符為新的字符ch:"+str);23 //replace(int start, int end, String s), 用新的字符串替換指定區(qū)域的字符串24 str.replace(4, 7, "AVA");25 System.out.println("用新的字符串替換指定區(qū)域的字符串:"+str);26 // reverse()內(nèi)容反轉(zhuǎn)27 str.reverse();28 System.out.println("內(nèi)容反轉(zhuǎn):"+str);29 //將字符串的子字符串復(fù)制給數(shù)組。30 char[] ch = new char[5];31 str.getChars(0, 4, ch, 0);32 System.out.println("將字符串的子字符串復(fù)制給數(shù)組:"+Arrays.toString(ch));33 34 35 36 37 StringBuilder str2=new StringBuilder(30);//創(chuàng)建一個長度為30的字符串38 str2.append("JAVA");39 System.out.println("字符串長度為:"+str2.length());//length(),獲取字符串長度40 System.out.println("字符串容量為:"+str2.capacity());//capacity(),獲取字符串的容量41 //有關(guān)字符串空間的方法42 //setLength(int newSize),設(shè)置字符串緩沖區(qū)大小43 str2.setLength(20);44 System.out.println("字符串長度為:"+str2.length());45 System.out.println("字符串容量為:"+str2.capacity());46 //ensureCapacity(int n),重新設(shè)置字符串容量的大小47 str2.ensureCapacity(20);48 System.out.println("字符串長度為:"+str2.length());49 System.out.println("字符串容量為:"+str2.capacity());50 str2.ensureCapacity(35);51 System.out.println("字符串長度為:"+str2.length());52 System.out.println("字符串容量為:"+str2.capacity());53 //trimToSize(),存儲空間縮小到和字符串長度一樣的長度54 str2.trimToSize();55 System.out.println("字符串長度為:"+str2.length());56 System.out.println("字符串容量為:"+str2.capacity());57 58 59 }60 61 }
運行結(jié)果:
結(jié)果分析:
1、在使用有范圍的參數(shù)方法時,要注意范圍包括開頭不包括結(jié)尾!
2、insert方法的位置是你要插入的位置,不是插入前一個位置!
3、getChars方法中注意字符數(shù)組的長度一定要大于等于begin到end之間字符的長度!
4、length是字符串內(nèi)容的長度,而capacity是字符串容量(包括緩存區(qū))的長度!
5、ensureCapacity方法是確保容量至少等于指定的最小值。如果當(dāng)前容量小于該參數(shù),然后分配一個新的內(nèi)部數(shù)組容量更大(不是你指定的值,系統(tǒng)自動分配一個空間)。如果當(dāng)前容量不小于該參數(shù),則容量不變。
6、trimToSize(),存儲空間縮小到和字符串長度一樣的長度。避免空間的浪費!
總結(jié)
(1).如果要操作少量的數(shù)據(jù)用 = String
(2).單線程操作字符串緩沖區(qū) 下操作大量數(shù)據(jù) = StringBuilder
(3).多線程操作字符串緩沖區(qū) 下操作大量數(shù)據(jù) = StringBuffer
參考文獻:
http://www.jb51.net/article/33398.htm
http://blog.csdn.net/mad1989/article/details/26389541
本文如果對大家的學(xué)習(xí)有幫助,請點擊下方的“推薦”或者“收藏”!您的支持將是我最大的動力,謝謝???(●˙?˙●)???再來一個不要臉的求“關(guān)注”
作者: 云開的立夏
出處: http://www.cnblogs.com/hysum/>
關(guān)于作者:本人目前還在上學(xué),小白一枚,希望能把學(xué)過的知識與大家分享,請多多賜教!
版權(quán)聲明:本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁面明顯位置給出 原文鏈接
大家寫文都不容易,請尊重勞動成果~這里謝謝大家啦(*/ω\*) 如有問題, 可郵件(hysum626@162.com)咨詢.
http://www.cnblogs.com/hysum/p/7125651.html