字符串String在Java中被设计成不可变是有多个原因的。
1. 字符串池(String Pool)的需求
字符串池(String intern pool)在方法区域中是一个特殊的存储区域。如果某字符串已经存在于字符串池中了,那么当创建同样内容的字符串时,就会返回该字符串的reference,而不会重新创建一个新的object。下面两行代码创建的字符串只会在内存的heap中创建一个字符串object
String str1 = "abcd";
String str2 = "abcd";
用一张图来形象地表示就是这样的:
如果字符串不是不可变的话(双重否定句,表示字符串可变),那么使用一个reference(比如str1)改变了字符串的值的话就会使其他reference所指向的字符串内容的变化。(对字符串的改变操作都会返回一个新的字符串,详细内容请见这里 )
2. 允许String缓存其哈希值
Java中会频繁使用字符串的哈希值。比如说HashMap。字符串的不可变性保证了哈希值一定是不变的,因此对哈希值进行缓存就不用担心其变化了。也就是说我们不需要在每次使用字符串的时候都去计算哈希值,这样带来的好处就是更加高效了
在String类中,有以下代码:
private int hash; //this is used to cache hash code.
3. 安全性
String在许多java类中广泛用于参数存在,比如网络连接、打开的文件等等。如果String不是不可变的,连接connection或文件file就可能被改变,从而导致严重的安全问题。方法method认为其连接的是一台机器,但实际上却不是。在反射Relection中可变的字符串也会导致安全问题,因为参数都是字符串类型的。
以下是一个例子:
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);
}