標籤:
情景一:不好的字串拼接習慣
起因是這樣的:一個大牛在寫了一篇關於java字串最佳化問題的講解,他提到:不要使用strObj+otherValue的方法將otherValue轉換為字串形式,因為底層操作會讓你嚇一跳的。那麼底層的實質是怎麼樣的呢?他的意思是這樣的:
比如: String s = "I have";
int total = 12;
Dog dog = new Dog(); //假設Dog類重寫了toString方法
String msg = s + total+dog;
在運行時,msg的指派陳述式會執行為: msg = new StringBuilder().append(s).append(total).append(dog.toString()).toString();
我們發現,運行時建立了一個匿名的StringBuilder對象,來拼接字串,儲存到緩衝區,最後調用toString方法返回拼接後的結果。顯然,StringBuilder在這裡小材大用了,而且消耗了記憶體資源,不可取。我發現很多人都喜歡寫類似 ""+num的代碼,我也是。所以以後需要注意了,特別是效能敏感的環境下。應該改用String.valueOf(value),或者封裝類型的toString方法,比如Double.toString(value)。
如果需要對字串進行頻繁的修改操作,那就使用StringBuilder,或者StringBuffer(安全執行緒的),從而避免大量的中間字串垃圾片段。
情景二:為什麼一個是true,一個是false
一位網友提到了這樣一個問題:
String str = "abc";
String str1 = "ab" + "c";
String str2 = "ab";
String str3 = str2 + "c"; //試試運行這段代碼,就會發現第一個為true 第二個為false ,why?
下面是我的解釋:
這是因為javac的編譯最佳化造成的。str1是由2個字串常量拼接的,常量一定是不會改變的量,那麼在編譯階段,javac就有膽量將它給簡化一下:str1就最佳化為:str1="abc",又因為str也是"abc",所以str和str1共用了記憶體據“abc”,所以str和str1都指向"abc"。由於==是淺比較,比較的是字串的記憶體位址,所以他們相等。
而str3的拼接中包含了一個變數str2,而變數是在運行時動態確定的,所以javac不敢再編譯時間對它最佳化。也就是說,str3會在運行時在堆中new出字串對象"abc",顯然它的地址和前面的"abc"的地址不一樣,所以是false。
我對上面的程式匯出jar後進行反編譯,驗證了我的猜想,結果
為了進一步證實我的猜想,我把上面的代碼改為:
String str = "abc"; final String str2 = "ab"; String str3 = str2 + "c"; System.out.println(str == str3); //列印出true
那麼問題來了,不是說str2是變數的嗎。怎麼這次也列印是true呢?注意我給str2加上了修飾符final,聲明str2位常量,那麼同樣的道理,javac也能保證str2不會改變,於是把str3最佳化為:str3="abc"。
反編譯class檔案:
注意:不要使用 == 去比較字串,因為字串時參考型別,== 比較的是記憶體位址,而不是字串內容。請使用equals() 或者equalsIgnoreCase()。
只有字串常量是共用記憶體的,而字串變數或者運行時動態產生的字串,則非如此。
關於java字串編譯最佳化問題