在Java中,我們有三種存儲(chǔ)字符串的方式:String柄驻、StringBuffer狐树、StringBuilder。這篇文章主要是整理一下他們各自的優(yōu)缺點(diǎn)和使用場(chǎng)景鸿脓。該問題也是面試中常常被問到的基礎(chǔ)知識(shí)抑钟。
它們之間的關(guān)系圖如下:
1.三者區(qū)別
1.1 是否可變
String定義的字符串不可被改變。 每次對(duì)String對(duì)象進(jìn)行改變的時(shí)候野哭,其實(shí)是Java新創(chuàng)建了一個(gè)對(duì)象在塔,然后將之前的指針指向的新的對(duì)象。這樣做不僅浪費(fèi)了很多空間拨黔,還降低了效率蛔溃。
而StringBuilder和StringBuffer是可變對(duì)象。它們都繼承自AbstractStringBuilder類篱蝇,在AbstractStringBuilder中也是使用字符數(shù)組保存字符串贺待。因此他們的類創(chuàng)建的字符串對(duì)象能夠被多次修改,而不產(chǎn)生新的未使用對(duì)象零截。他們倆可使用的方法也是相同的麸塞。
1.2 線程安全
StringBuilder是線程不安全的,StringBuffer是線程安全的涧衙。因此單線程下使用StringBuilder會(huì)效率更高哪工,多線程下使用StringBuffer會(huì)效率更高。操作小數(shù)據(jù)的話弧哎,還是String效率高雁比。
1.3 應(yīng)用場(chǎng)景
String使用場(chǎng)景:
- 字符串內(nèi)容不長發(fā)生改變的業(yè)務(wù)場(chǎng)景
- 比如:常量聲明、少量字符串拼接
StringBuffer應(yīng)用場(chǎng)景:
- 頻繁進(jìn)行字符串運(yùn)算時(shí)(拼接傻铣、替換章贞、刪除)
- 多線程時(shí)、因?yàn)樗С志€程安全
- 比如:XML解析非洲、HTTP參數(shù)的解析與封裝
StringBuilder應(yīng)用場(chǎng)景:
- 頻繁進(jìn)行字符串運(yùn)算時(shí)(拼接鸭限、替換、刪除)
- 單線程時(shí)两踏,因?yàn)樗恢С志€程安全
- 比如:SQL語句拼接败京、Json數(shù)據(jù)封裝
2.StringBuffer
了解StringBuffer之前,我們先回顧一下String的特點(diǎn)梦染,String字符串一旦定義就不能修改也不能擴(kuò)充的赡麦,而且朴皆,當(dāng)使用連接符對(duì)字符串進(jìn)行拼接的時(shí)候java會(huì)為字符串新開辟空間,這樣就造成了空間的浪費(fèi)泛粹。
為此StringBuffer作為解決方案應(yīng)運(yùn)而生遂铡,他是線程安全的可變字符序列。他是一個(gè)用final修飾的類晶姊,它繼承于Object扒接,實(shí)現(xiàn)了兩個(gè)接口。該接口定義后內(nèi)容可變们衙,大小可變钾怔,使用拼接不會(huì)產(chǎn)生新的空間。他就類似于一個(gè)字符串的緩沖區(qū)蒙挑。
2.1 容量和長度
StringBuffer有兩個(gè)屬性宗侦,一個(gè)是容量,一個(gè)是當(dāng)前長度忆蚀。容量代表的是能容納多大的字符串矾利,默認(rèn)是16個(gè)字符大小。
2.2 構(gòu)造方法
它有三個(gè)構(gòu)造方法蜓谋,一個(gè)是無參的(最常用)梦皮,一個(gè)是傳入整型參數(shù)的,用于指定容量桃焕,指定了多少剑肯,容量就變成多少。一個(gè)是字符串參數(shù)的观堂,用于指定內(nèi)容让网,容量是16+參數(shù)的長度。
new StringBuffer(); //容量為16
new StringBuffer(int capacity); //容量是int
new StringBuffer(String str); //容量是str.length()+16
2.3 常用方法
- append:追加
可以將任意的類型的數(shù)據(jù)添加到字符串的緩沖區(qū)师痕,返回值是StringBuffer溃睹,就是它緩沖區(qū)本身。
StringBuffer sb = new StringBuffer();
StringBuffer sb1 = sb.append("hello");
System.out.println(sb); //輸出hello
System.out.println(sb1); //輸出hello
System.out.println(sb == sb1); //輸出true
因此胰坟,直接sb.append()即可因篇,無需使用引用進(jìn)行接收。
StringBuffer sb = new StringBuffer();
sb.append(true).append(123).append("heihei"); //鏈?zhǔn)骄幊?System.out.println(sb); //輸出true123heihei
- insert:插入
此方法可指定位置插入任意類型數(shù)據(jù)笔横。從0開始計(jì)數(shù)竞滓。
sb.insert(5,"world");
- deleteCharAt:刪除指定位置的一個(gè)字符
public StringBuffer deleteCharAt(int index);
- delete(int start, int end ):刪除指定長度字符串
public StringBuffer delete(int start, int end);
- replace(int start, int end, String str):替換指定位置、長度字符串
public StringBuffer replace(int start, int end, String str);
- reverse():翻轉(zhuǎn)
public StringBuffer reverse();
- 字符串截取
public String substring(int start); //從start開始截取直到最后
pubic String substring(int start, int end); //從start截取吹缔,到end結(jié)束
注意返回的是字符串
2.4 String到StringBuffer的轉(zhuǎn)換
錯(cuò)誤轉(zhuǎn)換方式:
StringBuffer sb = "hello";
Stringbuffer sb = s;
正確的方式:
StringBuffer sb = new StringBuffer(s);
StringBuffer sb1 = new Stringbuffer();
sb1.append(s)
2.5 StringBuffer到String的轉(zhuǎn)換
1.通過構(gòu)造方法
String str = new String(buffer)
2.通過toString()
String str = buffer.toString()
任何引用類型調(diào)用toString都可以轉(zhuǎn)換成字符串商佑。
2.6 String與StringBuffer作為形參傳遞的不同
對(duì)于基本數(shù)據(jù)類型來說,形參的改變不影響實(shí)參厢塘,但是對(duì)于引用數(shù)據(jù)類型來說茶没,形參的改變直接改變了實(shí)際的參數(shù)肌幽。
但是字符串雖然是引用類型,但他是比較特殊的抓半,因?yàn)樗浅A课辜保鎯?chǔ)在字符串常量池中。是一種特殊的引用類型笛求,我們可以將它是為基本數(shù)據(jù)類型煮岁,所以當(dāng)字符串作為形參的時(shí)候,形參改變但實(shí)參是不改變的涣易。
StringBuffer是正常的引用類型,形參改變則實(shí)參改變冶伞。
3.可變?cè)?/h2>
StringBuffer(始于 JDK 1.0 )和StringBuilder(始于 JDK 1.5)都是為了提高字符串的拼接效率新症,直接使用String的+進(jìn)行拼接的話JVM會(huì)創(chuàng)建多個(gè)字符串對(duì)象,造成開銷浪費(fèi)响禽。他倆用法一樣徒爹,只不過Buffer是線程安全,Builder是線程不安全的芋类。
String源碼中有一個(gè)成員是隆嗅,使用了final修飾,意思是不能更改:
private final char value[];
而Buffer和Builder的成員同樣也是一個(gè)字符數(shù)組侯繁,但是沒有使用final修飾:
char value[];
所以這就是可變和不可變的基礎(chǔ)和前提胖喳。
當(dāng)要存入字符串的長度+value的長度-value的長度>0的時(shí)候,說明贮竟,此時(shí)的容量已經(jīng)不足以裝下新的字符串了丽焊。然后Java會(huì)將原來的字符數(shù)組復(fù)制一份(使用的是str.getchars()),然后開辟一個(gè)新的數(shù)組咕别,賦予新的容量技健,該容量是str.length()+value,最后return this惰拱,返回當(dāng)前對(duì)象雌贱。返回當(dāng)前對(duì)象的好處就是可以寫成鏈?zhǔn)骄幊獭?br> String只要已添加就去開辟新對(duì)象,而Buffer和Builder只有在容量不夠的時(shí)候才去開辟新對(duì)象偿短。
4.注意事項(xiàng)
StringBuilder sb1 = new StringBuilder("abc");
StringBuilder sb2 = new StringBuilder("abc");
System.out.println(sb1 == sb2);//false
System.out.println(sb1.equals(sb2));//false
第一個(gè)返回的是false是因?yàn)樗容^的是對(duì)象的地址值欣孤,而第二個(gè)返回的是false是因?yàn)镾tringBuilder沒有重寫equals方法,使用的是Object的比較方式翔冀,也就是比較地址值导街。所以,要想用值來比較纤子,應(yīng)該重寫equals方法搬瑰。
String str1 = "abc";
StringBuilder sb1 = new StringBuilder("abc");
System.out.println(str1.equals(sb1));//false
雖然string重寫了equals方法款票,但是,sb1對(duì)象不是String類的實(shí)例泽论,所以返回的也是false.
參考: