String/StringBuilder/StringBuffer
轉載自:http://www.cnblogs.com/xrq730/p/4841518.html
public class TestMain { public static void main(String[] args) { String str0 = "123"; String str1 = "123"; System.out.println(str0 == str1); } }
結果:true
public class TestMain { public static void main(String[] args) { String str2= new String("234"); String str3 = new String("234"); System.out.println(str2 == str3); } }
結果:false
原因:
在JVM中有一塊區域叫常量池,常量池中的數據是那些在編譯期間被確定,並被保存在已編譯的.class文件中的一些數據。除了包含所有的8種基本數據類型(char, byte, short, int, long, float, double, boolean)外,還有String及其數組的常量值,另外還有一些以文本形式出現的符號引用。
Java棧的特點是存取速度快(比堆塊),但是空間小,數據生命周期固定,只能生存到方法結束
1、true、c、123,這些等號右邊的指的是編譯期間可以被確定的內容,都被維護在常量池中
2、b、c、str這些等號左邊第一個出現的指的是一個引用,引用的內容是等號右邊數據在常量池中的地址
3、boolean、char、String這些是引用的類型
棧有一個特點,就是數據共享。回到我們第一個例子,第五行String str0 = "123",編譯的時候,在常量池中創建了一個常量"123",然後走第六行String str1 = "123",先去常量池中找有沒有這個"123",發現有,str1也指向常量池中的"123",所以第七行的str0 == str1返回的是true,因為str0和str1指向的都是常量池中的"123"這個字符串的地址。當然如果String str1 = "234",就又不一樣了,因為常量池中沒有"234",所以會在常量池中創建一個"234",然後str1代表的是這個"234"的地址。分析了String,其實其他基本數據類型也都是一樣的:先看常量池中有沒有要創建的數據,有就返回數據的地址,沒有就創建一個
第二個例子呢?Java虛擬機的解釋器每遇到一個new關鍵字,都會在堆內存中開辟一塊內存來存放一個String對象,所以str2、str3指向的堆內存中雖然存儲的是相等的"234",但是由於是兩塊不同的堆內存,因此str2 == str3返回的仍然是false,網上找到一張圖表示一下這個概念:
為什麽要使用StringBuilider和StringBuffer拼接字符串?
public class StringTest { @Test public void testStringPlus() { String str = "111"; str += "222"; str += "333"; System.out.println(str); } }
編譯器每次碰到"+"的時候,會new一個StringBuilder出來,接著調用append方法,在調用toString方法,生成新字符串。
那麽,這意味著,如果代碼中有很多的"+",就會每個"+"生成一次StringBuilder,這種方式對內存是一種浪費,效率很不好。
以StringBuilder為例:
public class TestMain { public static void main(String[] args) { StringBuilder sb = new StringBuilder("111"); sb.append("222"); sb.append("111"); sb.append("111"); sb.append("444"); System.out.println(sb.toString()); } }
StringBuffer和StringBuilder原理一樣,無非是在底層維護了一個char數組,每次append的時候就往char數組裏面放字符而已,在最終sb.toString()的時候,用一個new String()方法把char數組裏面的內容都轉成String,這樣,整個過程中只產生了一個StringBuilder對象與一個String對象,非常節省空間。StringBuilder唯一的性能損耗點在於char數組不夠的時候需要進行擴容,擴容需要進行數組拷貝,一定程度上降低了效率。
StringBuffer和StringBuilder用法一模一樣,唯一的區別只是StringBuffer是線程安全的,它對所有方法都做了同步,StringBuilder是線程非安全的,所以在不涉及線程安全的場景,比如方法內部,盡量使用StringBuilder,避免同步帶來的消耗。
另外,StringBuffer和StringBuilder還有一個優化點,上面說了,擴容的時候有性能上的損耗,那麽如果可以估計到要拼接的字符串的長度的話,盡量利用構造函數指定他們的長度。
String/StringBuilder/StringBuffer