前言
"我的風格比較偏傳統和經典" 小明說,"我們在打扮自己的問題上還是蠻冒險的...我覺得當你是只狗的時候,穿什么都hold的住!"
哈哈哈,脫離單身狗快兩年了,生活中除了愛情,不變的還有對代碼的摯愛,總之始於熱愛,忠於愛情,陷於代碼。
前半年規划人生,后半年開始規划,最近發生的一些事情還是讓自己倍感壓力的,生活可以知足常樂,但人生不可以,如果你不把生命體驗到極致,也許會被未來的自己所鄙視。
前世今生
String不可變這個話題應該是老生長談了,你可以說它就是設計者的龜腚,然后巴拉巴拉說出一大堆優點,也可以說它忠於愛情,只要JVM存活,它便萬年不變。
String自打娘胎一出生就跟他們的兄弟姐妹不一樣,好好的娃被戴了一個final的帽子,以至於byte,int,short,long等基本類型的小伙們都不帶它玩。
但是,String並不是一個簡單的人設,如果各位小伙伴們仔細查閱其源碼,那可是浩浩盪盪的3000+行代碼了,如果你一個controller能這樣的代碼量也是非常不錯的。
如果你仔細閱讀源碼注釋,你會發現這樣一句話:
Strings are constant; their values cannot be changed after they are created
大致意思就是String是個常量,從一出生就注定不可變。
我覺得到這里各位小伙們應該就知道為什么String不可變了,戴了個final的帽子,官方注釋說明創建后不能被改變,但是為什么String要使用final修飾呢?
面試精選
在了解String不可變之前,我覺得有必要分析一道經典的面試題:
public class Apple {
public static void main(String[] args) {
String a = "abc";
String b = "abc";
String c = new String("abc");
System.out.println(a==b);
System.out.println(a.equals(b));
System.out.println(a==c);
System.out.println(a.equals(c));
}
}
答案是:true、true、false、true
如圖所示,代碼中的abc對應個的abc:
我覺得有必要把a、b、c替換成小明、紅、光,abc替換成紅蘋果重新梳理一下流程:
-
小明想吃紅蘋果,小明的爸爸首先會在樹上尋找是否有紅蘋果,有的話,爸爸就說,這個蘋果歸你了,如果沒有,假設萬能的小明爸爸也可以給他造一個。
-
這時候小紅過來了,爸爸我也想吃那個紅蘋果,並且強烈要求要拿一個,就這樣小明和小紅共享了這個蘋果。
-
小光是個聽話的孩子,只要是紅蘋果就行,我可不想跟他倆爭什么,爸爸就這樣從超市里給小光買了一個紅蘋果。
-
小明和小紅的是同一個蘋果,這個是不變的事實,無論你怎么比較。
-
小紅,小明和小光的都是紅蘋果,但卻不是同一個蘋果。
-
你可以把蘋果樹理解成常量池,爸爸購買蘋果的過程理解為new對象,當然,舉例可能不是太恰當,只是為了大家更好的理解。
回到代碼本來來說,因為String太過常用,JAVA類庫的設計者在實現時做了個小小的變化,即采用了享元模式,每當生成一個新內容的字符串時,他們都被添加到一個共享池中,當第二次再次生成同樣內容的字符串實例時,就共享此對象,而不是創建一個新對象,但是這樣的做法僅僅適合於通過=符號進行的初始化。
需要說明一點的是,在object中,equals()是用來比較內存地址的,但是String重寫了equals()方法,用來比較內容的,即使是不同地址,只要內容一致,也會返回true,這也就是為什么a.equals(c)返回true的原因了。
不可變的好處
首先,我們應該站在設計者的角度思考問題,而不是覺得這不好,那不合理:
-
可以實現多個變量引用堆內存中的同一個字符串實例,避免創建的開銷。
-
我們的程序中大量使用了String字符串,有可能是出於安全性考慮。
-
大家都知道HashMap中key為String類型,如果可變將變的多么可怕。
-
當我們在傳參的時候,使用不可變類不需要去考慮誰可能會修改其內部的值,如果使用可變類的話,可能需要每次記得重新拷貝出里面的值,性能會有一定的損失。
其次,我們再分析有沒有更好的解決方案:
- 然,對於我來說,並沒有!!!
- 然,對於我來說,並沒有!!!
- 然,對於我來說,並沒有!!!
總結
了解到String是不可變的,知道了常量池是怎么個東西。
重溫了面試題,有興趣的小伙伴也可以去閱讀下String的源碼,浩浩盪盪的3000+。
String 被new時是要創建對象的,+ 號拼接同理,程序中盡量不要使用 + 拼接,推薦使用StringBuffer或者StringBuilder。