Java中String,StringBuffer,StringBuilder基礎知識

前言

在平時開發中,咱們不少時候都會用到StringStringBufferStringBuilde這三者。那麼這三者到底是什麼呢,下面一一講述。java

String

String
查看 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); 
		  
複製代碼

上面代碼str1str3是直接賦值的,str2是經過字符數組來構建一個字符串常量,str4是直接經過new關鍵字來建立對象。能夠看到用equals比較str1str2str3str4是相等的,用==比較str1str2str3str4是不相等的。這裏簡單說一下equals==的簡單區別:api

  • ==操做符的做用
  1. 比較基本數據類型是否相同
  2. 判斷引用是否指向堆內存的同一塊地址,也就是是否指向同一個對象
  • equasl方法的做用
  1. 判斷兩個變量是否對同一個對象的引用,也就是內存堆中的內容是否相同,其實就是對String對象所封裝的字符串內容做比較,具體的值是否相等,若是兩個String對象所封裝的字符型內容相同,則equals()方法將返回true

而上面的狀況由於str1str2是兩個不一樣的對象,可是內容相等,因此==返回了false,而equals返回true,str3str4也是同樣道理,從上面能夠簡單得出一個結論,不管上直接賦值仍是經過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); 
複製代碼

上面發現用==比較時,str8str10不是指向同一個地址,這是由於在編譯時str8str9就已經肯定了,而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圖一
這裏簡單說一下,由於一開始 String str11 = "This is Java";在常量池已經建立 This is Java這個常量,因此 String str12 = new String("This is Java");這句代碼不會再常量池建立對象,只會在堆上建立對象 This is Java,也就是說 new String建立字符串它其實分兩步操做:

  1. 在堆上建立對象
  2. 檢查常量池有沒有字符串字面量,若是沒有就在常量池建立常量,若是存在就不作任何操做

狀況五

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 圖2

狀況六

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

intern
因此上面代碼,用下面圖顯示:

String intern示意圖

總結

  1. String a = "xxx" 可能建立一個或者不建立對象,當xxx這個字符常量在String常量池不存在,會在String常量池建立一個String對象,而後a會指向這個內存地址,後面繼續用這種方式繼續建立xxx字符串常量,始終只有一個內存地址被分配。
  2. String a = new String("xxx")至少建立一個對象,也有可能兩個。只要用到new就確定在堆上建立一個String對象,而且檢查String常量池是否存在,若是不存在就會在Stirng常量池建立這個String對象,存在就不建立。

StringBuffer字符串變量

StringBuffer
api文檔上,一樣能夠知道 StringBuffer繼承 Object類,並實現了序列化接口,可以被添加 char序列和值的對象接口,可讀序列接口。和 String相比,沒有實現 Comparable,而是實現了 Appendable。它是 線程安全的可變字符序列,一個相似於 String的字符串緩衝區,可是不能修改,雖然在任意時間點上它都包含某種特定的字符序列,但經過某些方法調用的能夠改變該序列的長度和內容。 StringBuffer上的主要操做是 appendinsert方法,能夠重載這些方法,以接受任意類型的數據,每一個方法都能有效地將給定的數據轉換成字符串,而後將該字符串的字符追加或者插入到字符串緩衝區中。 append方法始終將這些字符添加到緩衝區的末端,而 insert方法則在指定的點添加字符。 StringBufferString不同,它是**字符串變量,是能夠改變的對象,當對字符串作操做時,就是在對象上作操做,不會像 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
複製代碼

StringBuilder字符串變量

StringBuilder示意圖
StringBuffer同樣,也是實現 SerializableAppendableCharSequence接口。一個可變的字符序列,這個類提供一個與 StringBuffer兼容的API,可是不保證同步,該類被設計用做 StringBuffer的一個簡易替換,用在字符串緩衝區被單個線程使用的時候。若是可能,建議優先採用該類,由於在大多數實現中,比 StringBuffer要快。在 StringBuilder 上的主要操做是 appendinsert方法,可重載這些方法,以接受任意類型的數據。每一個方法都能有效地將給定的數據轉換成字符串,而後將該字符串的字符追加或插入到字符串生成器中。 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

三者運行時間

總結

  • String :不可變類,任何對String的改變都會引起新的String對象的生成,適用於少許的字符串操做的狀況
  • StringBuffer :線程安全,任何對它所指代的字符串的改變都不會產生新的對象,適用多線程下在字符緩衝區進行大量操做的狀況
  • StringBuilder :線程不安全,所以不適合多線程中使用,適用於單線程下在字符緩衝區進行大量操做的狀況
  • 速度運行方面:StringBuilder > StringBuffer > String
相關文章
相關標籤/搜索