前言:
字符串(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
對象的引用。它遵循如下規則:
對於任意兩個字符串s
和t,
當且僅當s.equals(t)
爲true
時,s.intern() == t.intern()
才爲tr
ue
。」
示例代碼:
app
1
2 String name = new String("fancy");
3
4 if(name == "fancy") { // false
5 System.out.println("equals 1");
6 }else {
7 System.out.println("not equals 1"); // Be printed
8 }
9
10 name = name.intern(); //將字符串添加到駐留池
11
12 if(name == "fancy") { // true
13 System.out.println("equals 2"); // Be printed
14 }else {
15 System.out.println("not equals 2");
16 }
17 編程語言
輸出結果:ide
1
2not equals 1
3equals 2
4
性能
由上面的示例代碼能夠看到,字符串駐留池的使用是很是簡單的,池中的對象能夠被共享,只要你將字符串添加到池中,就可以直接使用。
"==" 來比較兩個對象,而不是隻能使用equals來做比較。將字符串添加到駐留池來使用 "==" 做比較的方式要比直接使用 equals效率要高些。
再論String、StringBuffer和StringBuilder測試
由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 趟的總耗時
8 while(COUNT -- > -1) {
9 // 也能夠測試每趟都建立一個新的對象,這樣 StringBuilder 效率比 StringBuffer 的效率變得更明顯了
10
14 System.out.println("----------------------------------<</span>" + (COUNT + 1) + ">");
15 beginTime = System.currentTimeMillis();
16 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();
24 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對象。