Java String以及字符串直接量與字符串駐留池

前言:
字符串(String)是java編程語言中的核心類之一,在咱們日常時候使用也比較很廣泛,應用普遍。
但你是否知道什麼是字符串直接量,知不知道有個字符串駐留池,字符串的駐留池能夠用來緩存字符串直接量。
java

什麼是直接量
直接量是指:在程序中,經過源代碼直接指定的值。
eg:
int personId = 8080 ;
String name = "fancy" ;

對於Java中的字符串直接量,JVM會使用一個字符串駐留池來緩存它們。通常狀況下,字符串駐留池中的字符串對象不會被GC(Garbage Collection,垃圾回收)所回收。當再次使用字符串駐留池中已有的字符串對象的時候,無需再次建立它,而直接使用它的引用變量(類類型)指向字符串駐留池中已有的字符串對象

編程

String基礎緩存

String類表明字符串。字符串是常量,它們的值在建立以後是不能再被更改的。在Java中除了synchronized以外,不可變的類也是線程安全的,所以,String自己也是線程安全的。String類的實例對象實際上是能夠被共享的。
例代碼 安全

String name = "fancy";           // @1
String nick    = "fancydeepin";      // @2
name = nick; 
System.out.println(name);
結果輸出 :fancydeepin

這是怎麼回事?不是說String是不可變的字符串嗎?怎麼這裏又變了?
是這樣的,在這裏name只是一個引用類型(類類型,即引用數據類型)變量,並非一個String對象,@1中建立了一個 "fancy" 的字符串對象,@2中建立了一個 "fancydeepin" 的字符串對象,name 引用 (就像一個指針) 剛開始是指向 "fancy" 對象,然後,name 又從新指向 "fancydeepin" 對象,在示例代碼中,整個過程只建立了兩個 String 對象 (不知道我這樣說你能不能理解,爲何是隻建立了兩個 String 對象?而不是 1個、3個...  @3 ),一個是 "fancy" 對象,另一個是"fancydeepin"對象。而這兩個對象被建立出來後並無被改變過,之因此程序會輸出fancydeepin,徹底只是由於
name引用指向對象發生了改變。

若是你是本着認真的態度看着個人貼子,細心的你,是否會留意到:
當name引用從新指向另一個對象的時候,那name以前引用的對象 ( "fancy" 對象 ) JVM 在底層會怎麼處理它呢?是會當即來回收它來釋放系統資源嗎?
答案是否認的。雖然這時候程序不再訪問"fancy"這個對象,但JVM仍是不會來回收它,它將在程序運行期間久駐內存,爲何會這樣呢?
再往下說就扯到Java的內存管理機制了,這裏點到即止。在這裏你能夠簡單的將它理解成 "fancy" 對象被緩存了起來 ( 實際上也是因爲被緩存了 )。

字符串駐留池
當比較兩個String對象時候,是應該用 "==" 呢?仍是應該選擇equals呢?相信絕大部分人絕大多時候使用的都是選擇用equals方法。
"=="和equals的用法相信你們都很熟悉了,"==" 比較的是兩個對象的哈希碼值是否相等,而equals比較的是對象的內容是否同樣。
而絕大部分時候咱們比較兩個String對象的時候只是想比較它們的內容是否相等,這樣看來,只能選equals了,但真的是這樣嗎?
答案是否認的。你同樣也能夠用 "==" 來完成這樣的一件事情,並且 "==" 的效率不管如何都是要比使用equals的效率要高的,但前提是,須要使用字符串的駐留池,才能使用 "==" 來替代equals做比較。
String裏面有個方法叫intern(),執行效率很高,但也許你還未曾用過,下面是摘自API中intern()方法的描述:
「當調用 intern方法時,若是池已經包含一個等於此String對象的字符串(用equals(Object)方法肯定),則返回池中的字符串
不然,將此String對象添加到池中,並返回此String對象的引用。
它遵循如下規則:
對於任意兩個字符串st,當且僅當s.equals(t)true時,s.intern() == t.intern()
才爲true。」

例代碼:
app

 1
 2           String name = new String("fancy");
 3            
 4java <wbr>String以及字符串直接量與字符串駐留池        if(name == "fancy") {                            // false
 5            System.out.println("equals 1");
 6java <wbr>String以及字符串直接量與字符串駐留池        }else {
 7            System.out.println("not equals 1");    // Be printed
 8        }
 9        
10        name = name.intern();    //將字符串添加到駐留池
11        
12java <wbr>String以及字符串直接量與字符串駐留池        if(name == "fancy") {    // true
13            System.out.println("equals 2");        // Be printed
14java <wbr>String以及字符串直接量與字符串駐留池        }else {
15            System.out.println("not equals 2");
16        }
17 編程語言


輸出結果:ide

1
2not equals 1
3equals 2
4
性能

由上面的示例代碼能夠看到,字符串駐留池的使用是很是簡單的,池中的對象能夠被共享,只要你將字符串添加到池中,就可以直接使用。
"==" 來比較兩個對象,而不是隻能使用equals來做比較。將字符串添加到駐留池來使用 "==" 做比較的方式要比直接使用 equals效率要高些。

再論String、StringBufferStringBuilder測試

由synchronized修飾的方法能夠保證方法的線程安全,可是會下降該方法的執行效率;
ui

翻開API,很容易就能知道:StringBuffe是線程安全的可變字符序列,StringBuilder是一個可變的字符序列,是線程不安全的;
網上說的所謂的使用StringBuffer的效率更高更好,這已經不合時宜,這是java 1.5以前的版本的說法,早過期瞭如今!
如今是反過來了,因爲StringBuilder不是線程安全的,StringBuilder效率會比StringBuffer效率更高一些。

你能夠不相信我說的,但你總該相信程序跑出來的結果吧,下面是示例代碼:

 1
 2        StringBuffer  buffer  = new StringBuffer();
 3        StringBuilder builder = new StringBuilder();
 4        int COUNT = 10;       // 測試 COUNT 趟
 5        final int N = 100000// 每趟操做 N 次
 6        double beginTime, costTime; // 每趟開始時間和耗費時間
 7        double bufferTotalTime = 0.0D, buliderTotalTime = 0.0D// StringBuffer 和 StringBuilder 測試 COUNT 趟的總耗時
 8java <wbr>String以及字符串直接量與字符串駐留池        while(COUNT -- > -1) {
 9            // 也能夠測試每趟都建立一個新的對象,這樣 StringBuilder 效率比 StringBuffer 的效率變得更明顯了
10java <wbr>String以及字符串直接量與字符串駐留池           
14            System.out.println("----------------------------------<</span>" + (COUNT + 1+ ">");
15            beginTime = System.currentTimeMillis();
16java <wbr>String以及字符串直接量與字符串駐留池            for(int i = 0; i <</span> N; i++) {
17                buffer.append(i);
18                buffer.length();
19            }
20            costTime = System.currentTimeMillis() - beginTime;
21            bufferTotalTime  += costTime;
22            System.out.println("StringBuffer  費時: --->> " + costTime);
23            beginTime = System.currentTimeMillis();
24java <wbr>String以及字符串直接量與字符串駐留池            for(int i = 0; i <</span> N; i++) {
25                builder.append(i);
26                builder.length();
27            }
28            costTime = System.currentTimeMillis() - beginTime;
29            buliderTotalTime += costTime;
30            System.out.println("StringBuilder 費時: --->> " + costTime);
31            System.out.println("----------------------------------<</span>" + (COUNT + 1+ ">");
32        }

33        System.out.println("bufferTotalTime / buliderTotalTime = " + (bufferTotalTime / buliderTotalTime));
34


後臺輸出結果:

 1
 2----------------------------------<</span>10>
 3StringBuffer  費時: --->> 32.0
 4StringBuilder 費時: --->> 16.0
 5----------------------------------<</span>10>
 6----------------------------------<</span>9>
 7StringBuffer  費時: --->> 21.0
 8StringBuilder 費時: --->> 15.0
 9----------------------------------<</span>9>
10----------------------------------<</span>8>
11StringBuffer  費時: --->> 25.0
12StringBuilder 費時: --->> 35.0
13----------------------------------<</span>8>
14----------------------------------<</span>7>
15StringBuffer  費時: --->> 24.0
16StringBuilder 費時: --->> 8.0
17----------------------------------<</span>7>
18----------------------------------<</span>6>
19StringBuffer  費時: --->> 48.0
20StringBuilder 費時: --->> 38.0
21----------------------------------<</span>6>
22----------------------------------<</span>5>
23StringBuffer  費時: --->> 22.0
24StringBuilder 費時: --->> 8.0
25----------------------------------<</span>5>
26----------------------------------<</span>4>
27StringBuffer  費時: --->> 23.0
28StringBuilder 費時: --->> 9.0
29----------------------------------<</span>4>
30----------------------------------<</span>3>
31StringBuffer  費時: --->> 25.0
32StringBuilder 費時: --->> 7.0
33----------------------------------<</span>3>
34----------------------------------<</span>2>
35StringBuffer  費時: --->> 23.0
36StringBuilder 費時: --->> 7.0
37----------------------------------<</span>2>
38----------------------------------<</span>1>
39StringBuffer  費時: --->> 78.0
40StringBuilder 費時: --->> 59.0
41----------------------------------<</span>1>
42----------------------------------<</span>0>
43StringBuffer  費時: --->> 21.0
44StringBuilder 費時: --->> 11.0
45----------------------------------<</span>0>
46bufferTotalTime / buliderTotalTime = 1.6056338028169015
47


StringBuffer在測試中平均耗時是StringBuilder的1.6倍以上,再測屢次,都是1.6倍以上。StringBuffer和StringBuilder的性能孰更加優,一眼明瞭。

最後,若是你想知道@3 (在上面我已經用紅色粗體標出) 的結果,不妨說一下:
eg:
String mail = "fancydeepin" + "@" + "yeah.net";

來,一塊兒來看一下上面的這條語句,想一下,這條語句將會建立幾個String的對象呢?
1個? 2個? 3個? 4個? 5個? ... ...

也許你會認爲是4個,它們分別是:"fancydeepin"、"@"、"yeah.net"、"fancydeepin@yeah.net"
也許你會認爲是5個,它們分別是:"fancydeepin"、"@"、"yeah.net"、"fancydeepin@"、"fancydeepin@yeah.net"
也許 ... ...

但實際上,這條語句只建立了一個String對象!
爲何會這樣呢?緣由很簡單,這是由於,mail在編譯的時候其值就已經肯定,它就是 "fancydeepin@yeah.net" 。
當程序處於運行期間且當上面的這條語句被執行到的時候,那麼mail所引用的對象就會被建立,而mail的值因爲在編譯的時候已經肯定,它是"fancydeepin@yeah.net",因此最終只有一個String對象被建立出來,而這個對象就是"fancydeepin@yeah.net" 對象。

這樣解釋都可以理解了吧?真的理解了嗎?是真的理解纔好,不妨再來看一個:
eg:
String mail = new String("fancydeepin@yeah.net");

這回又建立了幾個對象呢?
答案是2個。爲何不是1個了呢?2個又是哪2個呢?
能夠很確定的告訴你,它們分別是: "fancydeepin@yeah.net"、new String()
這是由於,這回mail在編譯的時候它的值是還不可以肯定的,編譯只是將源代碼翻譯成字節碼,程序還並無跑起來,還new不了對象,因此在編譯完成以後,mail的值是還不可以肯定的。
當程序處於運行期間且當上面的這條語句被執行到的時候,這時候纔開始去肯定 mail 的引用對象,首先,"fancydeepin@yeah.net"對象會被建立。
以後,再執行new String(),因此這條語句最後其實是建立了2個String對象。

相關文章
相關標籤/搜索