1. 程式人生 > >String/StringBuilder/StringBuffer

String/StringBuilder/StringBuffer

har 常量值 數據 基本數據類型 class文件 jvm 原因 用法 保存

轉載自: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棧的特點是存取速度快(比堆塊),但是空間小,數據生命周期固定,只能生存到方法結束

。我們定義的boolean b = true、char c = ‘c‘、String str = “123”,這些語句,我們拆分為幾部分來看:

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