在Java中字符串是一个不可变的类,简单地说,一个不可变的类就是指它的实例不可被修改。当一个实例被创建的时候它的所有信息都被初始化,并且这些信息不可以被修改。不可变的类有很多优点,这篇文章概括了为什么字符串被设计成不可变的。基于对内存、同步、数据结构等知识的理解,我们可以得到一个满意的解答。
在方法区中字符串池(字符串保留池)是一个特殊的存储区,每当创建一个字符串并且这个字符串已经存在于池中,这个已存在的字符串的引用将被返回,而不是创建一个新的对象并返回它的引用。
下面的代码将在堆中只创建一个字符串对象。
String string1 = "abcd"; String string2 = "abcd";
如果一个字符串是可变的,那么改变一个带有引用的字符串,将会导致该对象的其它引用的错误。
2.缓存Hashcode(散列码)
在Java中字符串的hashcode经常被用到。例如用在HashMap中,字符串不可变保证了hashcode一直都是一样的,因此无需担心它的改变就可以将它缓存。这意味着使用它的时候无需每次都计算hashcode,这是更加高效的,代码如下:
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";
在这个例子中,如果String是可变的,它的值的改变将会违反集合(set)的设计(set中的元素是不重复的)。这个例子只是为简单起见而设计的,实际上字符串类并没有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.不可变的对象自然是线程安全的
因为不可变的对象不能被修改,所以可以在多个线程中自由地使用它们,这减少了使用同步的需求。
总的来说,字符串设计成不可变是为了效率和安全的考虑,这也是通常偏向于使用不可变的类的原因。