为什么在java中字符串不是不可变的

Tags: java

在 Java 中字符串是不可变的类。一个不可变类表示不能对该类的实例进行修改。实例中的所有信息都在实例初始化时创建并且该实例初始化后信息不可以被修改。不可变类有很多优点。这篇文章总结了为什么字符串被设计为不可变的。一个好的答案取决于堆内存、同步、数据结构等的深刻理解。

 1.字符串池的需求

字符串池是方法区域中的特殊的存储区域。当字符串被创建时如果该字符串已存在于该池那么将返回现有字符串的引用,而不是创建一个新对象并返回其引用。

下面的代码执行后,在堆中只有一个字符串对象。

String string1 = "abcd";
String string2 = "abcd";

下图解释了以上代码执行后的堆状态:

java-string-pool

如果字符串是可变的,那么通过引用string1改变字符串将会导致string2引用错误的值。

2.缓存Hashcode

在 Java 中经常使用的字符串的hashcode。例如,HashMap。字符串的不可变性保证hashcode始终相同,所以hashcode可以被缓存而不用担心字符串被修改。这意味着,每次使用时无需重新计算hashcode。这样效率更高。
String类的实现中有以下代码:

private int hash;//this is used to cache hash code.

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";

在这个例子中,如果字符串是可变的,那么它的值就可以改变这就会违反Set的设计(Set不能包含重复的元素)。此示例设计是为了说明问题,在真正的 String 类中有没有value字段。

4. 安全

字符串经常作为参数使用,如网络连接、打开文件等。如果字符串是可变的,则Connection或文件将可以改变,这会导致严重的安全威胁。比如,方法认为正连接到一台机器,但并不是。可变字符串可能导致安全问题,在反射中也一样,如果字符串作为参数使用。
下面是一个代码示例:

boolean connect(string s){
    if (!isSecure(s)) { 
throw new SecurityException(); 
}
    //here will cause problem, if s is changed before this by using other references.    
    causeProblem(s);
}

5.不可变的对象本质上都是线程安全的

因为不能更改不可变的对象,所以他们可以自由的在多个线程之间共享,这样可以减少同步。
总之,字符串不可变是为了效率和安全性。这也是为什么不可变类一般是首选的原因。

本文链接:http://www.4byte.cn/learning/120043/wei-shi-me-zai-java-zhong-zi-fu-chuan-bu-shi-bu-ke-bian-de.html