前言
在Java中,字符串是一个不可变的类,一个不可变的类指的是它的实例对象不能被修改,所有关于这个对象的信息在这个对象被创建时已初始化且不能被改变。
不可变类有很多优势,这篇文章总结了字符串类之所以被设计成不可变类的原因,涉及内存模型,线程同步,数据结构等知识。
字符串常量池的需求
字符串常量池是方法区中的一块特别存储区域。当需要创建一个字符串时,如果它的值在字符串常量池中已存在,那么常量池中的该字符串引用将被直接返回,而不会创建一个新的字符串对象返回其引用。
String string1 = "abcd";
String string2 = "abcd";
如果字符串是可变的,那么通过一个引用改变字符串的值,其它该字符串对象的引用将得到错误的值。
缓存Hashcode
字符串的hashcode在java中经常被使用,如HashMap。不可变保证了hashcode会一直相同,所以它可以缓存起来而不需要担心改变。这也意味着,每次使用String的时候,不需要重新计算hashcode,这使得性能更加有效,所以我们经常会看到map的key使用字符串。
在String类中,有一个hash字段用于缓存hash code。
private int hash;//this is used to cache hash code.
方便其它对象的使用
为了更加具体的说明,看下下面的程序:
HashSetset = new HashSet (); set.add(new String("a")); set.add(new String("b")); set.add(new String("c")); for(String a: set) a.value = "a";
在这个例子中,如果字符串是可变的,那么它的value会被改变,这将违背Set集合的设计(不允许重复元素)。当然,这仅仅是一个示例,实际情况,String是没有value字段的。
安全性
字符串作为参数被广泛使用,如网络连接,文件打开操作等。如果字符串是可变的,那么网络连接或者文件将会被改变,这将引起严重的安全威胁。某个方法被认为连接到某一台机器,但是却被改变了,连接到其它机器。在反射中,可变的字符串同样会引起安全问题,因为参数同样是字符串。
如下代码是一个例子:
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); }
不可变的对象天生线程安全
因为不可变对象不能被修改,它们可以被多线程共享,这消除了同步操作。
总的来说,字符串被设计成不可变是出于性能和安全考虑,这也是为什么通常不可变类比较受欢迎。
译文链接:http://www.programcreek.com/2013/04/why-string-is-immutable-in-java/