Java 为什么String不可变?
20
08 月

Java 为什么String不可变?

在Java中,String是不可变类。一个不可变的类,其实例不能被修改。在创建一个实例时,实例中的所有信息被初始化且不能被修改。不可变类有许多优点。本文总结了为什么String被设计为不可变 。一个好的答案取决于对内存、同步及数据结构等的深刻理解

1. 字符串池的要求(Requirement of String Pool)

字符串池(或字符串驻留池,Requirement of String Pool)是方法区中一个特殊的存储区域。当创建一个字符串,如果在字符串池里已存在,已有字符串的引用将被返回,而不是创建一个新对象并返回其引用。

下面的代码将在堆(heap)中创建只有一个字符串对象。

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

它将如下:

String-Immutability-2

如果字符串不是不可变的,改变一个引用指向值将改变另外一个引用的值。

2. Hashcode 缓存(Caching Hashcode)

字符串的哈希码在Java中经常使用。例如,在HashMap中不可变保证了哈希码将始终相同,故无需担心变化。 这意味着,没有必要每次使用的时候来计算哈希码,提高了效率。

在String类中,有以下代码:

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

3. 促进其他对象的使用(Facilitating the Use of Other Objects)

为了具体化,考虑下面的程序:

HashSet set = new HashSet();
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. 安全

在许多Java类中,字符串被广泛用作参数,例如网络连接、打开文件等。字符串若可变,连接或文件将被改变,并导致严重的安全威胁。该方法认为是连接到一台机器,其实不是。可变字符串也可能导致的反馈安全问题,因为参数都是用字符串来表示。

下面是一个代码示例:

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. 不可变对象天生具备线程安全性

因为不可改变的对象不能被改变,它们可以在多个线程之间自由共享。这消除了同步的要求。

综上所述, String被设计为不可变是为效率和安全性着想,这也是为什么不可变的类在通常情况下优先的的原因。