Java 中的 String 真的是不可變的嗎
阿新 • • 發佈:2018-12-13
我們都知道 Java 中的 String 類的設計是不可變的,來看下 String 類的原始碼。
public final class String implements java.io.Serializable, Comparable<String>, CharSequence { /** The value is used for character storage. */ private final char value[]; /** Cache the hash code for the string */ private int hash; // Default to 0 // ... }
可以看出 String 類是 final 型別的,String 不能被繼承。其值 value 也就是對字元陣列的封裝,即 char[],其值被定義成 private final 的,說明不能通過外界修改,即不可變。
String 真的 "不可變 " 嗎?
來看下面這個例子。
String str = "Python";
System.out.println(str); // Python
str = "Java";
System.out.println(str); // Java
str = str.substring(1);
System.out.println(str); // ava
你有可能會問:str 不是由 Python 變成 Java 了嗎?然後通過 substring 方法變成 ava 了嗎?
這其實是初學者的一個誤區,從上面看 String 的結構可以得知字串是由字元陣列構成的,str 只是一個引用而已,第一次引用了 "Python",後面變成了 "Java",而 substring 也是用 Arrays.copyOfRange 方法重新複製字元陣列構造了一個新的字串。
所以說,這裡的字串並不是可變,只是變更了字串引用。
關於 substring 在 JDK 各個版本的差異可以看這篇文章《注意:字串substring方法在jkd6,7,8中的差異》,也可以去看 substring 的各個版本的原始碼。
String 真的真的真的 "不可變 " 嗎?
上面的例子肯定是不可變的,下面這個就尷尬了。
String str = "Hello Python";
System.out.println(str); // Hello Python
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
char[] value = (char[])field.get(str);
value[6] = 'J';
value[7] = 'a';
value[8] = 'v';
value[9] = 'a';
value[10] = '!';
value[11] = '!';
System.out.println(str); // Hello Java!!
通過反射,我們改變了底層的字元陣列的值,實現了字串的 “不可變” 性,這是一種騷操作,不建議這麼使用,違反了 Java 對 String 類的不可變設計原則,會造成一些安全問題。