“ 为什么 String
定义为 final
” 的其它问法:why String is immutable in Java ?
或 why String is final in Java ?
。
虽然问题是短短的一句话:“为什么 String
定义为 final
?”。但问题表面下知识点可不止一点两点,为了更好的理解并回答这个问题。
一、 String
类 final
在什么地方?
...
* Strings are constant; their values cannot be changed after they
* are created. String buffers support mutable strings.
* Because String objects are immutable they can be shared.
...
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[];
...
}
在 String
类中的注释有这样一句话 Strings are constant;their values cannot be changed after they are created. ... Because String objects are immutable they can be shared.
。
String
类被 final
修饰,说明 String
类不能被继承,即没有人可以通过拓展(extending
)或重写(overriding
)来破坏 String
的不可变性(immutability
)、缓存(cache
)和哈希值(hashCode calculation
)。
二、回答这个问题的角度
- 内存(
memory
) - 同步(
synchronization
) - 数据结构(
data structure
)
1、字符串常量池(String pool
)
字符串常量池是方法区中一个特殊的内存区域。在创建字符串时,如果该字符串已经存在于常量池,则返回现有字符串的引用,而不是创建一个新的对象。
例如,如下例子只会创建一个字符串,而不是两个。
String s1 = "abc";
String s2 = "abc";
JVM的行为图如下
如果字符串是可变的,修改一个一个引用的字符串值将会导致另一个引用值也发生变化,这明显是不可以的。
2、哈希值(caching hashcode
)
字符串的hashCode
被频繁的用在hashMap
和hashSet
中。不可变性保证了hashCode
字符串一旦创建,hashCode
就不会再发生变化,不用每次都需要重新计算hashCode
(实际上,对象的hashCode
会延后到真正调用Object::hashCode()
方法时才计算),这使得进行与hashCode
有关的操作时更加有效率。
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
是可变的,修改value
值将会导致set
集合中存在重复的元素,这会违反set
集合中不包含重复值的设计。
4、安全
String
被广泛用作许多java类的参数,例如网络连接、打开文件等。如果字符串不是不可变的,连接或文件将被更改,这将导致严重的安全威胁。该方法认为它连接到一台机器上,但不是。可变字符串也可能在反射中导致安全问题,因为参数是字符串。
字符串是不可变的最重要的原因是类加载机制使用了它,因此具有深刻和基本的安全性。如果字符串是可变的,一个请求加载java.io
。Writer
可能会被改成mil.vogo.diskerasingwriter
5、不可变的对象天生就是线程安全的
因为不可变对象不能被更改,所以它们可以在多个线程之间自由地共享。这就消除了同步的需求。