JAVA基礎:String、StringBuffer、StringBuilder全面分析

三者就使用層面的簡略分析:
三者就字符串拼接方面運行速度快慢爲:StringBuilder > StringBuffer > String。 
線程安全上:StringBuilder是線程不安全的,而StringBuffer是線程安全的,原因StringBuffer中使用了很多synchronized。
小結:(1)如果要操作少量的數據用 String;
(2)多線程操作字符串緩衝區下操作大量數據 StringBuffer;
(3)單線程操作字符串緩衝區下操作大量數據 StringBuilder;

 

三者的主要繼承關係:都實現了Serializable(略)

 

三者字符串可變與不可變問題:
String是不可變長度的字符串,
StringBuffer是可變長度的字符串,
StringBuilder是效率更高的可變長度的字符串。

從源碼中分析(略):
String:

String 中用了final來修飾value爲不可變的。
先提出一個問題:string是final修飾的是不可變的,爲何利用"+"後,返回了一個拼接後改變的字符串?前後不矛盾?詳細請看下個 string的"+"問題  的分析。
StringBuffer:

分析:可以看見只要創建StringBuffer()對象,至少也分配了16個字符的空間。 

這裏的value沒有被final修飾

*String創建對象問題:

		//例題四
		String s5="abc";        //創建1個對象abc
		String s6="abc"+"de";   //創建2個對象de,abcde
		String s7="abcde";      //未創建對象
		System.out.println(s6==s7);//true
		
	//面試(重點重點):
		//例題五
		//題目:程序依次執行分別創建了多少新對象?
		String a1=new String("我");
		String a2="我";
		String a3=new String("我");
/*		答案:從上到下依次執行程序,
		     a1創建2個對象
		     a2未創建對象
		     a3創建1個對象
		解析:
		    a1:常量池中創建了"我",堆中創建了一個String類的對象。
		    (說明:new創建String對象時,需要傳入"我"這個字符串,因此會首先去常量池中尋找"我",因爲沒有,所以就在常量池中創建了"我"這個字符串及對象)
		    a2 :直接引用a1在常量池中創建的"我"。
		    a3:同理,只創建了一個新的String類對象*/

 

*(重點)String典型的“+”問題:


  public static void main(String[] args)  {
	        String a = "hello2";
	        final String b = "hello";	        
	        String c = b + 2;	       
	        String f ="hello"+"2";
	        
	        String d = "hello";
	        String e = d + 2;
	        String g = d + 2;
	        //第一類問題:直接字符串拼接
	        System.out.println(a == c);// true 
	        System.out.println(a == f);// true 

	        //第二類問題:字符串變量+字符串
	        System.out.println(a == e);// false
	        System.out.println(e == g);// false
       
 
    }

 解析:
a==c : 由於final的修飾,b在編譯期間就已經是一個確定值!由於各變量都爲確定值,因此b+2="hello"+"2"="hello2",注意這是在編譯時完成的(自動對代碼的優化),拼接好後由於常量池中已經存在"hello2"的字符串,因此不會創建新的字符串,最後c即指向"hello2",實際直接指向的是常量池的"hello2",a最早也指向常量池的"hello2",因此 a、c指向同一個地址。
a==f:同理。

a==e:編譯期間不知道d是什麼,會自動創建了一個StringBuffer對象,依次append(d).append("2"),最後toString給e。a指向的是常量池,而e指向的是對象及是指向的堆!因此a、e指向的是兩個地方,肯定不可能相等。
超級經典的例子讓你明白橫線上的問題:

String s=null;
s=s+"abc";
System.out.println(s);//nullabc

e==g:如果你看明白了a==e那這裏就太簡單了,因爲創建了兩個不同的StringBuffer對象,e、g分別指向這兩個對象!因此地址肯定不等!

 

 

*StringBuffer擴容問題:

StringBuffer初始化及擴容機制

1.StringBuffer()的初始容量可以容納16個字符,當該對象的實體存放的字符的長度大於16時,實體容量就自動增加。StringBuffer對象可以通過length()方法獲取實體中存放的字符序列長度,通過capacity()方法來獲取當前實體的實際容量。

2.StringBuffer(int size)可以指定分配給該對象的實體的初始容量參數爲參數size指定的字符個數。當該對象的實體存放的字符序列的長度大於size個字符時,實體的容量就自動的增加。以便存放所增加的字符。

3.StringBuffer(String s)可以指定給對象的實體的初始容量爲參數字符串s的長度額外再加16個字符。當該對象的實體存放的字符序列長度大於size個字符時,實體的容量自動的增加,以便存放所增加的字符。

擴容算法:
使用append()方法在字符串後面追加東西的時候,如果長度超過了該字符串存儲空間大小了就需要進行擴容:構建新的存儲空間更大的字符串,將久的複製過去;

再進行字符串append添加的時候,會先計算添加後字符串大小,傳入一個方法:ensureCapacityInternal 這個方法進行是否擴容的判斷,需要擴容就調用expandCapacity方法進行擴容

嘗試將新容量擴爲大小變成2倍+2   if 判斷一下 容量如果不夠,直接擴充到需要的容量大小。

 

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