字串是編程中應用到最多的一個資料類型,簡單實用,但是深入理解它們,會給我們帶來更多好處。
字串緩衝池:
為了節省記憶體,提高資源的複用,java中引入了字串緩衝池的概念。
緩衝池中的字串是不可回收的:
在緩衝池中的字串是不會被記憶體回收機制回收的,基本都是常駐記憶體,所以過多食用String類,可能會出現記憶體溢出(下面會講)。
怎麼樣才會存進緩衝池:
- 直接量賦值建立對象
在Java,基本封裝類型String,Long,Float,Boolean.......都可以利用直接量建立對象。
String用直接量進行建立對象的時候,會先在緩衝池找到字串相同的對象,然後指向緩衝池中該對象,這樣就避免了重新為該對象分配記憶體,從而提高了複用,所以建議多用直接量去建立對象,這個也是上篇博文所推薦的。
- 調用構造器建立對象
這種方法調用構造器,凡是new出來的對象都要進行記憶體配置,所以是不會指向緩衝池之前已有的對象,這樣就導致緩衝池裡面可能有多個值相同的String對象。
package biaodashi;/** * 字串關於緩衝池 * * @author ccf * */public class StringHuanCunChi { /** * @param args */ public static void main(String[] args) { // TODO Auto-generated method stub StringHuanCunChi huanCunChi = new StringHuanCunChi(); huanCunChi.test1(); } public void test1() { String str1 = "ccf"; String str2 = "ccf"; String str3 = new String("ccc"); // new 會有新的地址分配,所以不會指向緩衝池的"ccc" String str4 = new String("ccc"); System.out.println("str1==str2?" + (str1 == str2)); System.out.println("str3==str4?" + (str3 == str4)); } }運行結果str1==str2?truestr3==str4?false
結果:看出str1 和str2都是指向同一個對象 而str3和str4就不是同一個對象了。
怎麼樣才能指向緩衝池已有的對象:
要指向緩衝池對象,改對象必須是直接賦直接量,也可以是多個直接量的運算值,而不能調用方法或者其他變數,但是被final修飾的可以進行宏替換的常量也可以看成直接賦值,因為以上這一些都能在加在類的時候,就被虛擬機器計算出來其值。所以能指向緩衝池。
public void test1() { String str1 = "ccf"; String str2 = "c" + "c" + "f";//三個直接量進行運算 System.out.println("str1==str2?" + (str1 == str2)); 結果:str1==str2?true 多個直接量進行串連,也可以指向緩衝池
上面的代碼中 String str2 = "c" + "c" + "f"
;一共建立了幾個對象?????
可能有人會說n個,但是其實只有一個,因為虛擬機器對他們進行直接的計算,不需要建立對象,這也是使用直接量的好處。
public void test1() { String str1 = "ccf" + "長度為:" + "3"; String str2 = "ccf" + "長度為:" + str1.length(); System.out.println("str1==str2?" + (str1 == str2)); // 結果: str1==str2?false 結論:str2調用了方法str1.length(),故不能再虛擬機器加在類的時候就計算確定str的值,所以列印結果如下 }public void test1() { final String length1 = "3"; String length2 = "3"; String str1 = "ccf" + "長度為:" + "3"; String str2 = "ccf" + "長度為:" + length1;//調用final變數 String str3 = "ccf" + "長度為:" + length2;//調用普通變數 System.out.println("str1==str2?" + (str1 == str2)); System.out.println("str1==str3?" + (str1 == str3)); 結果: str1==str2?truestr1==str3?false 可以得出結論:被final修飾的 length1效果是和直接量一樣的,在虛擬機器加在時候,length1是直接替換"3",之後就跟length1沒關係了,效果可以說跟str1中一摸一樣,這也是final變數和其他變數的不同之處,稱為宏替換。
關於String的不可變問題:
String
} 類是個不可變的類,建立對象後,不可以改變對象,但是日常代碼經常對String進行操作,這對於String不可變這一說法是不是相悖了? 看下這段代碼:
public void testString() { String str1 = "Hello"; int OldId = System.identityHashCode(str1); str1 = str1 + " ccf!";//對字串進行操作 int NewId = System.identityHashCode(str1); System.out.println("OldId=" + OldId + "\nNewId=" + NewId); } 結果:OldId=854453928NewId=584020407 其中System.identityHashCode(str1);函數是列印出對象的HashCode,每個對象都有一個唯一的HashCode,是對象的唯一標識,很明顯,前後兩個HashCode是不同的,
得出一些結論:對String對象進行操作後,其返回的是一個新的對象,之前那個對象是沒有改變的,改變的是str這個引用所指的對象,這時候的對象已經是新的對象,然而之前那個對象被廢棄了,但是他存在緩衝池,因此不會被記憶體回收機制回收,所以這裡會出現記憶體流失,所以操作字串,盡量不用String。 但是StringBuffer和StringBuilder進行字串操作的時候,就不會去new出現對象,引用的都是同一個對象,就可以減少String帶來的弊端。
public void testStringBuilder() { StringBuilder builder = new StringBuilder("Hello"); System.out.println("ID=" + System.identityHashCode(builder)); builder.append(" ccf!"); System.out.println("ID=" + System.identityHashCode(builder)); System.out.println(builder); } 結果:ID=854453928ID=854453928Hello ccf!