[细说Java](2)Java中字符串为什么是不可变的
最后更新于:2022-04-01 10:00:14
在Java中字符串(String)是一个不可改变的类。一个不可改变的类只是一个对象实例不可修改的简单类。当创建一个对象实例时,对象实例的所有信息都被初始化,并且信息不能被修改。对于不可改变的类来说还有很多优势。上一篇文章很好说明了为什么字符串被设计成不可改变的。只有你很好的掌握了内存,同步,数据结构等知识后,你才能很好的回答这个问题。
1.字符串常量池的需求
字符串常量区是方法区(Method Area)中一块特殊的存储区域。当一个字符串被创建,如果该字符串已经存在字符串常量区时,相应的返回存在字符串的引用,而不是去新创建一个新的对象返回它的引用。
下面的代码中只会在堆中创建一个字符串对象。
~~~
String string1 = "abcd";
String string2 = "abcd";
~~~
下面的图能很好的说明:
![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-03-18_56eba2a98c48c.jpg)
如果字符串是可以改变的,改动一个字符串的引用将会导致另一个引用出现错误值。
2.缓存HashCode(哈希码)
在Java中一个字符串的哈希码是经常被使用的,例如在hashMap中。不可改变性确保了哈希码总是会保持不变,这样我们就不必担心发生任何变化。换一句话说,我们没有必要每次使用字符串时都要计算哈希码(因为不可改变性确保哈希码保持不变),这是一种更有效率的方式。
在String类中,都会有下列代码:
~~~
public final class String implements java.io.Serializable, Comparable<String>, CharSequence{
/** Cache the hash code for the string */
private int hash; // Default to 0
}
~~~
3.促进其他的对象的使用
为了说明更具体一些,考虑下面的程序:
~~~
HashSet<String> set = new HashSet<String>();
set.add(new String("a"));
set.add(new String("b"));
set.add(new String("c"));
for(String a: set)
a.value = "a";
~~~
在这个例子中,假设String是可变的,那么其值就可以修改,这有违集(set)的设计(集中不包含重复的元素)。这个例子为简单考虑,在真实String类中没有value属性。
4.安全
String在很多Java类中广泛使用作为参数,例如,网络连接,打开文件等等。如果字符串是可改变的,那么网络连接或者文件都可能会被改变,将导致严重的安全威胁。方法还以为是连接到一台机器上,但实际上却没有。可变字符串可能会导致在反射中出现问题,因为这些参数都是字符串。
~~~
boolean connect(string s){
if (!isSecure(s)) {
throw new SecurityException();
}
//如果在这之前s通过其他引用进行改变,这可能会出现问题
causeProblem(s);
}
~~~
5.不可变对象是线程安全的
因为不可变对象是不可改变的,那么它们可以在多个线程之间自由的共享。这就取消了进行同步的需求。
总之,字符串被设计为不可变是为了效率和安全性考虑。这也就是一般情况下优先选择不可变对象的原因。
原文:[Why String is immutable in Java ?](http://www.programcreek.com/2013/04/why-string-is-immutable-in-java/)