String是Java中的不可变类。不可变类即其对象是不修改的,对象一旦建立,就不可改变。不可变类有许多优点,下面简要介绍一下String为什么是不可变类,对内存、同步、数据结构的认识会有助于理解。
1.String常量池的需要
常量池(String intern pool)是方法区(method area)中的一个特殊区域。当一个String被创建时,如果该字符串在常量池中已存在,那么将会返回该字符串对象的引用,而不是重新创建一个新String对象并返回其引用。
下面的代码只会在堆中创建一个对象:
String string1="abcd";
<span style="white-space:pre"> </span>String string2="abcd";
用一张图来说明:
如果String是可变的,那么其中一个引用改变了字符串的值,将会导致另一个对该字符串的引用出错。
2.保存哈希码的需要
字符串的哈希码在Java中经常用到,如HashMap。不可变保证了哈希码永远是相同的,不必担心保存哈希码的改变。这样,就不必每次使用时都计算字符串的hashcode,保证了效率。
在String类里,有如下的代码:
<span style="white-space:pre"> </span>private int hash;//this is used to cache hash code
3.使其他对象的使用更加容易
为了更具体地说明,请看下面的代码:
HashSet<String> set = new HashSet<String>();
<span style="white-space:pre"> </span>set.add(new String("a"));
<span style="white-space:pre"> </span>set.add(new String("b"));
<span style="white-space:pre"> </span>set.add(new String("c"));
<span style="white-space:pre"> </span>for(String a: set)
<span style="white-space:pre"> </span>a.value = "a";
在这个例子中,如果String是可变的,其值的改变就会引起set设计的冲突(set用来存储不重复的元素)。这个例子只是用来简单的说明,在真实的String类里没有value域。
4.安全性
String在许多java内置类里作为参数被广泛应用,如network connection,open files等等。如果String是可变的,将导致连接或文件面临严重的安全威胁。方法可能会将某个连接误认为是对某台机器的连接,但实际上可能是另一台机器的。同时,可变类也会影响反射的安全性。
下面是一个例子:
<span style="white-space:pre"> </span>boolean connect(string s){
<span style="white-space:pre"> </span>if (!isSecure(s)) {
<span style="white-space:pre"> </span>throw new SecurityException();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>//here will cause problem, if s is changed before this by using other references.
<span style="white-space:pre"> </span>causeProblem(s);
<span style="white-space:pre"> </span>}
5.不可变对象是天然线程安全的
因为不可变对象不能被修改,因此可以在多个线程间共享而不用进行任何同步的操作。
简单来说,String被设计为不可变类的原因就是效率和安全。这也是它被广泛使用的原因之一。