Java源碼之String,StringBuffer,StringBsuilder詳解

查看更多文章 我的博客網站 www.caoyong.xin

Java中的字符串有兩類 一種是String,一種是StringBuffer/StringBuilder,一般知道對於字符串拼接,String類使用的是+,而StringBuffe/StringBuilder使用的是append方法,使用後者的效率要比前者高,我們還知道StringBuffer是線程安全的,StringBuilder是線程不安全的,所以StringBuffer效率要比StringBuilder低。針對上面兩個問題我們只知道表面,卻不知道其中的門門道道。所以接下來就從源碼的角度分析上面兩種情況產生的原因所在。


目錄

 1:爲什麼StringBuffer/StringBuilder要比String的效率高?

 2:爲什麼StringBuffer是線程安全,而StringBuilder不是線程安全?


  1:爲什麼StringBuffer/StringBuilder要比String的效率高?

   關於這個問題首先我們用代碼來直觀體驗一下?

    代碼如下:

     public class Test {
    public static void testString() {
        System.out.println("==========================================");
        String a = new String();
        long start = System.currentTimeMillis();
        for(int i = 0 ;i<100000 ; i++) {
            a += "a";
        }
        System.out.println("String所花時間"+String.valueOf(System.currentTimeMillis()-start));
    }
    
    public static void testStringbuffer() {
        System.out.println("==========================================");
        StringBuffer a = new StringBuffer();
        long start = System.currentTimeMillis();
        for(int i = 0 ;i<100000 ; i++) {
            a.append("a");
        }
        System.out.println("Stringbuffer所花時間"+String.valueOf(System.currentTimeMillis()-start));
    }
    
    public static void testStringBuilder() {
        System.out.println("==========================================");
        StringBuilder a = new StringBuilder();
        long start = System.currentTimeMillis();
        for(int i = 0 ;i<100000 ; i++) {
            a.append("a");
        }
        System.out.println("Stringbuilder所花時間"+String.valueOf(System.currentTimeMillis()-start));
    }
    public static void main(String[] args) {
        Test.testString();
        Test.testStringbuffer();
        Test.testStringBuilder();
    }
}

運行上述代碼:

 ==========================================
String所花時間4984
==========================================
Stringbuffer所花時間15
==========================================
Stringbuilder所花時間15


可以看到在循環次數較大的情況下,StringBuffer/StringBuilder要比String所花時間少的多。而且再把循環次數調到大點,有可能就會報內存溢出異常。

 對於String來說有一句話比較經典“String是不變的”,怎麼個不變法?

 首先對於上面的問題先放一放,來說一下String。

  在Java中String定義爲final類型,說明不能被繼承,簡單點說,對於如此高頻率被使用的數據類型,設計者們認爲已經設計的足夠優秀了,不需要被繼承,否則胡亂繼承重寫可能會降低程序的性能。其次對於String類型來說不管是拼接使用的+還是String自帶的方法如replace,substring等方法。返回的都是一個新的String類型對象引用,從在內存中重新開闢一個空間存放新的String類型對象,以及一個新的對象引用。下面來看一個例子

 public class Test1 {
 
    public static void change(String str) {
        //執行replace方法後的str是一個新的引用
        str = str.replace("A", "a");
        //執行substring方法後又是一個新的引用
        str = str.substring(1);
    }
    
    public static void main(String[] args) {
        
        String str = "AABBCC";//創建一個str對象
        change(str);//調用change方法
        System.out.println(str);//注意這裏調用的是String str = "AABBCC"的str引用
    }
}

輸出結果是AABBCC 而不是aBBCC.。這也就印證了,對於String類型來說不管是拼接使用的+還是String自帶的方法如replace,substring等方法。返回的都是一個新的String類型對象引用,從在內存中重新開闢一個空間存放新的String類型對象,以及一個新的對象引用。


所以對於String來說每次的拼接字符串都要在內存中開闢新空間,存放拼接後的字符串,創建新引用。這一部分在頻繁操作後,會消耗大量時間


而對於StringBuffer/StringBuilder來說他們是怎樣拼接字符串的了?

查看StringBuffer/StringBuilder的源碼,(StringBuffer/StringBuilder源碼大致一樣)

 QQ截圖20180503220821.png

這是append方法。我們繼續點擊super.append(str),然後繼續點擊,直到來到最底層

QQ截圖20180503220930.png

可以看到這裏是用來System.arraycopy()方法。該方法是複製一個數組(ariginal)中的一部分放到(copy)數組中的指定位置(關於該方法可以查看API)。所以這裏就可以看到對於StringBuffer/StringBuilder的append方法,他其實是複製需要拼接的字符串,放到原來字符串的後面。而不是產生一個新的字符串。所以花費的時間就比較短,效率也就很高了。


2:爲什麼StringBuffer是線程安全,而StringBuilder不是線程安全?

  還是看源碼。我們看StringBuffer的源碼,對於StringBuffer類中所有的方法都使用了synchronized關鍵字。而synchronized關鍵字就是同步的意思。也就保證的在使用Stringbuffer的時候所有的線程都是同步的,即線程安全的

QQ截圖20180503222111.png

而對於StringBuilder確沒有synchronized關鍵字,所以它不是線程安全的。對於StringBuffer來說他是線程安全的,那麼既然要保證線程安全,那麼就會付出一些代價,花費的時間就會比StringBuilder要多一下。我們把最開始的例子循環次數在加大一些。(testString()就不要加大了不然會崩潰


public static void testStringbuffer() {
        System.out.println("==========================================");
        StringBuffer a = new StringBuffer();
        long start = System.currentTimeMillis();
        for(int i = 0 ;i<10000000 ; i++) {
            a.append("a");
        }
        System.out.println("Stringbuffer所花時間"+String.valueOf(System.currentTimeMillis()-start));
    }
    
    public static void testStringBuilder() {
        System.out.println("==========================================");
        StringBuilder a = new StringBuilder();
        long start = System.currentTimeMillis();
        for(int i = 0 ;i<10000000 ; i++) {
            a.append("a");
        }
        System.out.println("Stringbuilder所花時間"+String.valueOf(System.currentTimeMillis()-start));
    }


輸出結果:

==========================================
Stringbuffer所花時間473
==========================================
Stringbuilder所花時間142

這樣就看出對比來了。

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