在Java中String類為什麼要設計成final?String真的不可變嗎?其他基本型別的包裝類也是不可變的嗎?
最近突然被問到String為什麼被設計為不可變,當時有點懵,這個問題一直像bug一樣存在,竟然沒有發現,沒有思考到,在此總結一下。
1.String的不可變
String類被final修飾,是不可繼承和修改的。當一個String變數被第二次賦值時,不是在原有記憶體地址上修改資料,而是在記憶體中重新開闢一塊記憶體地址,並指向新地址。
String類為什麼要被設計為是final的?
1.不可變性支援執行緒安全。
2.不可變性支援字串常量池,提升效能。
3.String字串作為最常用資料型別之一,不可變防止了隨意修改,保證了資料的安全性。
正常情況下Java的String字串是final且不可變的。不過可以通過特殊手段修改它的內容。
String類的主力成員欄位value是個char[ ]陣列,而且是用final修飾的。final修飾的欄位建立以後就不可改變。因為雖然value是不可變,也只是value這個引用地址不可變。擋不住Array陣列是可變的事實。Array的資料結構看下圖:
也就是說Array變數只是stack上的一個引用,陣列的本體結構在heap堆。String類裡的value用final修飾,只是說stack裡的這個叫value的引用地址不可變。沒有說堆裡array本身資料不可變。
程式碼測試:
1 String test = "immutable String"; 2 String test1 = test; 3 String test2 = new String(test); 4 String test3 = new String(test.toCharArray()); 5 Field values = String.class.getDeclaredField("value"); 6 values.setAccessible(true); 7 char[] chars = (char[])values.get(test); 8 chars[0] = 'u'; 9 chars[1] = 'n'; 10 System.out.println("test==test1:" + (test == test1)); 11 System.out.println("test==test2:" + (test == test2)); 12 System.out.println("test1==test2:" + (test1 == test2)); 13 System.out.println("test:" + test + " test1:" + test1 + " test2:" + test2 + " test3:" + test3);
由String的不可變性引申到其他基本資料型別: Byte,Short,Integer,Long,Double,Float,Character,Boolean 八種基本資料的包裝類,仔細檢視發現也是final修飾的,再仔細檢視一下enum列舉型別,發現用javac編譯後再用javap反編譯也是被編譯為final修飾的類,並且其列舉值全部定義為static final 修飾的成員變數。
由此發現,Java設計者在設計Java基本資料型別時,把基本資料型別全部設計為不可變的,這樣既方便了開發人員,又保證了資料的安全性。
總結:Java中String是不可變的,但是可以通過反射修改其內容。
備註:
作者:Shengming Zeng
部落格:https://blog.csdn.net/Z645817
本文是原創,歡迎大家轉載;但轉載時必須註明文章來源,且在文章開頭明顯處給明連結。
<歡迎有不同想法或見解的同學一起探討,共同進步>