在平時開發中,咱們不少時候都會用到String
,StringBuffer
,StringBuilde
這三者。那麼這三者到底是什麼呢,下面一一講述。java
api
文檔,能夠知道,
String
是繼承
object
類,並實現了序列化接口,字符序列接口,字符串排序接口。
String
是Java中的字符串,Java程序中的全部字符串字面值(如"abc")都做爲此類的實例實現,字符串是常量,它們的值在建立以後不能更改,字符串緩衝區支持可變的字符串。由於
String
對象是不可變的,全部能夠共享,例如:
String str1 = "abc";
等效於
char data[] = {'a','b','c'};String str2 = new String(data);
,由於
String
是不可變的對象,每次對
String
類型改變的時候其實都等同於生成一個新的
String
對象,而後將指針指向新的
String
對象,因此常常改變內容的字符串最好不要用
String
,由於每次生成對象都會對系統性能產生影響,當內存多了無引用的對象,
JVM
的GC就會工做,頻繁GC就會形成界面的卡頓,下面直接上例子:
String str1 = "abc";
char data[] = {'a','b','c'};
String str2 = new String(data);
//輸出true
System.out.println(str1.equals(str2));
//輸出false
System.out.println(str1 == str2);
String str3 = "This is Java";
String str4 = new String("This is Java");
//輸出true
System.out.println(str3.equals(str4));
//輸出false
System.out.println(str3 == str4);
複製代碼
上面代碼str1
和str3
是直接賦值的,str2
是經過字符數組來構建一個字符串常量,str4
是直接經過new
關鍵字來建立對象。能夠看到用equals
比較str1
和str2
,str3
和str4
是相等的,用==
比較str1
和str2
,str3
和str4
是不相等的。這裏簡單說一下equals
和==
的簡單區別:api
String
對象所封裝的字符串內容做比較,具體的值是否相等,若是兩個String
對象所封裝的字符型內容相同,則equals()
方法將返回true
。而上面的狀況由於str1
和str2
是兩個不一樣的對象,可是內容相等,因此==
返回了false,而equals
返回true,str3
和str4
也是同樣道理,從上面能夠簡單得出一個結論,不管上直接賦值仍是經過new
關鍵字來建立String
對象,都會生成兩個不一樣的String
對象,即便字符型內容相同。數組
String str5 = "This is Java";
String str6 = "This is";
String str7 = "This is" + " Java";
//輸出true
System.out.println(str5.equals(str7));
//輸出true
System.out.println(str5 == str7);
複製代碼
從上面代碼能夠看出,不管是用==
或者equals
來比較兩個沒有引用而且內容值是相等返回都是true,也就是String str7 = "This is" + " Java";
這句話沒有建立新的對象,而是引用指向str5
的地址。安全
String str8 = "This is Java";
String str9 = "This is";
String str10 = str9 + " Java";
//輸出true
System.out.println(str8.equals(str10));
//輸出false
System.out.println(str8 == str10);
複製代碼
上面發現用==
比較時,str8
和str10
不是指向同一個地址,這是由於在編譯時str8
和str9
就已經肯定了,而str10
是引用變量,不會再編譯時肯定,因此會建立String
對象。多線程
String str11 = "This is Java";
String str12 = new String("This is Java");
String str13 = str12;
//輸出true
System.out.println(str11.equals(str12));
//輸出true
System.out.println(str11.equals(str13));
//輸出true
System.out.println(str12.equals(str13));
//輸出false
System.out.println(str11 == str13);
//輸出false
System.out.println(str11 == str12);
//輸出true
System.out.println(str12 == str13);
複製代碼
由於三個String
對象的值都是相等的,因此用equals
來比較是返回true,由於str13
是指向str12
,至關於傳遞引用,因此用==
來比較是相等的,都是同一個對象,用一張圖來比較直觀:app
String str11 = "This is Java";
在常量池已經建立
This is Java
這個常量,因此
String str12 = new String("This is Java");
這句代碼不會再常量池建立對象,只會在堆上建立對象
This is Java
,也就是說
new String
建立字符串它其實分兩步操做:
String str14 = "This is Java";
String str15 = "This is Java";
String str16 = "This is Java";
//輸出true
System.out.println(str14.equals(str15));
//輸出true
System.out.println(str14.equals(str16));
//輸出true
System.out.println(str15.equals(str16));
//輸出true
System.out.println(str14 == str15);
//輸出true
System.out.println(str14 == str16);
//輸出true
System.out.println(str15 == str16);
複製代碼
直接上圖:性能
String str17 = new String("This is Java");
String str9 = str17.intern();
String str18 = "This is Java";
//輸出false
System.out.println(str9 == str17);
//輸出true
System.out.println(str9 == str18);
複製代碼
上面調用了intern
這個方法,這句話就是把字符串對象加入常量池中,實際操做以下,直接上圖吧:ui
String a = "xxx"
可能建立一個或者不建立對象,當xxx
這個字符常量在String
常量池不存在,會在String
常量池建立一個String
對象,而後a
會指向這個內存地址,後面繼續用這種方式繼續建立xxx
字符串常量,始終只有一個內存地址被分配。String a = new String("xxx")
至少建立一個對象,也有可能兩個。只要用到new
就確定在堆上建立一個String
對象,而且檢查String
常量池是否存在,若是不存在就會在Stirng
常量池建立這個String
對象,存在就不建立。api
文檔上,一樣能夠知道
StringBuffer
繼承
Object
類,並實現了序列化接口,可以被添加
char
序列和值的對象接口,可讀序列接口。和
String
相比,沒有實現
Comparable
,而是實現了
Appendable
。它是
線程安全的可變字符序列,一個相似於
String
的字符串緩衝區,可是不能修改,雖然在任意時間點上它都包含某種特定的字符序列,但經過某些方法調用的能夠改變該序列的長度和內容。
StringBuffer
上的主要操做是
append
和
insert
方法,能夠重載這些方法,以接受任意類型的數據,每一個方法都能有效地將給定的數據轉換成字符串,而後將該字符串的字符追加或者插入到字符串緩衝區中。
append
方法始終將這些字符添加到緩衝區的末端,而
insert
方法則在指定的點添加字符。
StringBuffer
和
String
不同,它是**字符串變量,是能夠改變的對象,當對字符串作操做時,就是在對象上作操做,不會像
String
同樣建立額外的對象進行操做。
StringBuffer str20 = new StringBuffer("This is");
//StringBuffer後面追加內容
str20.append(" Java");
System.out.println(str20); //輸出:This is Java
//刪除第6-8個字符
str20.delete(5, 8);
System.out.println(str20); //輸出:This Java
//在9個位置插入
str20.insert(9," is good");
System.out.println(str20); //輸出:This Java is good
//替換5-9
str20.replace(5, 9, "C++");
System.out.println(str20); //輸出:This C++ is good
//倒序
str20.reverse();
System.out.println(str20); //輸出:doog si ++C sihT
//變成大寫
System.out.println(str20.toString().toUpperCase()); //輸出:DOOG SI ++C SIHT
//第一種方案StringBuffer轉化成String
String str21 = new String(str20);
System.out.println(str21); //輸出:doog si ++C sihT
//第二種方案
String str22 = str20.toString();
System.out.println(str22); //輸出:doog si ++C sihT
複製代碼
StringBuffer
同樣,也是實現
Serializable
,
Appendable
,
CharSequence
接口。一個可變的字符序列,這個類提供一個與
StringBuffer
兼容的API,可是不保證同步,該類被設計用做
StringBuffer
的一個簡易替換,用在字符串緩衝區被單個線程使用的時候。若是可能,建議優先採用該類,由於在大多數實現中,比
StringBuffer
要快。在
StringBuilder
上的主要操做是
append
和
insert
方法,可重載這些方法,以接受任意類型的數據。每一個方法都能有效地將給定的數據轉換成字符串,而後將該字符串的字符追加或插入到字符串生成器中。
append
方法始終將這些字符添加到生成器的末端;而
insert
方法則在指定的點添加字符。將
StringBuilder
的實例用於
多個線程是不安全的。若是須要這樣的同步,則建議使用
StringBuffer。
主要一些操做:
StringBuilder str23 = new StringBuilder("This is");
//StringBuilder後面追加內容
str23.append(" Java");
System.out.println(str23); //輸出:This is Java
//刪除第6-8個字符
str23.delete(5, 8);
System.out.println(str23); //輸出:This Java
//在9個位置插入
str23.insert(9," is good");
System.out.println(str23); //輸出:This Java is good
//替換5-9
str23.replace(5, 9, "C++");
System.out.println(str23); //輸出:This C++ is good
//倒序
str23.reverse();
System.out.println(str23); //輸出:doog si ++C sihT
//變成大寫
System.out.println(str23.toString().toUpperCase()); //輸出:DOOG SI ++C SIHT
//第一種方案StringBuffer轉化成String
String str24 = new String(str23);
System.out.println(str24); //輸出:doog si ++C sihT
//第二種方案
String str25 = str23.toString();
System.out.println(str25); //輸出:doog si ++C sihT
複製代碼
//作賦值操做
//String賦值
String str = new String("I Love Android");
long starttime1 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
str = str + "!";
}
long endtime1 = System.currentTimeMillis();
System.out.println("String花費的時間 :" + (endtime1 - starttime1));
//StringBuilder
StringBuilder str3 = new StringBuilder("I Love C++");
long starttime3 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
str3 = str3.append("!");
}
long endtime3 = System.currentTimeMillis();
System.out.println("StringBuilder花費的時間 :" + (endtime3 - starttime3));
//StringBuffer
StringBuffer str2 = new StringBuffer("I Love Java");
long starttime2 = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
str2 = str2.append("!");
}
long endtime2 = System.currentTimeMillis();
System.out.println("StringBuffer花費的時間 :" + (endtime2 - starttime2));
複製代碼
運行截圖:spa