1. String 不可變
-
不可變對象
對象在創(chuàng)建完成之后嫂拴,其狀態(tài)不能再被改變矿辽,則該對象即為不可變對象
-
對象不可變具體內(nèi)容
不能改變對象內(nèi)的成員變量
基本數(shù)據(jù)類型的值不能改變
引用類型的變量不能指向其他對象
引用類型所指向的對象狀態(tài)也不能改變
-
例如
String s = "ABC"; System.out.print(s); // ABC s = "123"; System.out.print(s); // 123
-
執(zhí)行結(jié)果
"ABC" "123"
釋義
對象:在內(nèi)存中是一塊內(nèi)存區(qū)弦撩,成員變量越多艇潭,這塊內(nèi)存區(qū)占的空間越大。
引用:只是一個4字節(jié)的數(shù)據(jù)贡翘,里面存放了它所指向的對象的地址篷就,通過這個地址可以訪問對象辩恼。
- s只是一個引用宾娜,它指向了一個具體的對象偏形,當s=“123”; 這句代碼執(zhí)行過之后仙粱,又創(chuàng)建了一個新的對象“123”蚪黑, 而引用s重新指向了這個新的對象膏斤,原來的對象“ABC”還在內(nèi)存中存在徐绑,并沒有改變。
- 不能直接操作對象本身莫辨,所有的對象都由一個引用指向傲茄,必須通過這個引用才能訪問對象本身,包括獲取成員變量的值沮榜,改變對象的成員變量盘榨,調(diào)用對象的方法等
- 效率和安全
Java 將 String 設成不可變最大的原因是效率和安全
- 提高字符串常量池的效率和安全性
- 如果一個對象是不可變的 ,需要拷貝的對象的內(nèi)容時就不用復制它本身蟆融,而只是復制它的地址草巡,復制地址(通常一個指針的大小)需要很小的內(nèi)存,效率也很好
- 對于引用同一個對象的其他變量也不會造成影響
- 對于多線程是安全的型酥。多線程同時進行時山憨,一個可變對象的值很可能被其他線程改變
2. String 源碼
-
JDK1.7以后,String 類的成員變量只剩下兩個:
String 類成員變量.png - 釋義
- String 類實際上是對字符數(shù)組的封裝弥喉。
- 成員變量 hash郁竟,是該 String 對象的哈希值的緩存。
-
在 Java 中由境,數(shù)組也是對象棚亩,所以 value 也只是一個引用蓖议,它指向一個真正的數(shù)組對象。在執(zhí)行第一句代碼時讥蟆,真正的內(nèi)存排布如下圖:String 內(nèi)存數(shù)據(jù).png
- String 對象不可變
- String 內(nèi)部成員變量 value 和 hash 并未對外提供 setter 方法勒虾,且變量 value 是 final 的。這些都說明一旦 String 內(nèi)部的 value 被初始化后攻询,便不會再改變从撼,故而 String 對象不會改變。
-
至于 String 內(nèi)部其他方法:如 replace() 在執(zhí)行后钧栖,結(jié)果發(fā)生的變化低零,也是同理。即:這些方法執(zhí)行過程中拯杠,會生成新的 String 對象掏婶,該對象將會被賦值給引用,而原來的對象并未改變潭陪。如:String 新對象.png
-
值得注意的是雄妥,通過反射可以修改對象的值。如:
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { String s = "Hello World"; System.out.println("s: " + s); // 獲取String類中的value字段 Field valueFieldOfString= String.class.getDeclaredField("value"); // 設置訪問權(quán)限 valueFieldOfString.setAccessible(true); // 獲取 s 對象上的value屬性的值 char[] value = (char[]) valueFieldOfString.get(s); // 改變value所引用的數(shù)組中的第 5 個字符 value[5] = '_'; System.out.println("s: " + s); // Hello_World }