最近突然被問到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
博客:http://www.cnblogs.com/zengming/
本文是原創,歡迎大家轉載;但轉載時必須注明文章來源,且在文章開頭明顯處給明鏈接。
<歡迎有不同想法或見解的同學一起探討,共同進步>