相信大部分准备过java面试的同学都知道Java中的String是final修饰,不可变的。可是只知道是什么,不知道为什么是一件非常危险的事情。因为任何事情都没有绝对。
我们先看看什么是不可变对象。
什么是不可变对象?
如果一个对象,在创建完成之后,不能在改变他的状态。(包括内部成员变量,基本数据类型。引用类型的变量不能指向其他对象,引用类型指向的对象的状态)对象则认为不可改变。
关于Java中的String不可变的原因,具体可以参考下面这篇文章。
文中源码一直分析到了1.7,但是1.8之后,String源码又改动。
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
// The associated character storage is managed by the runtime. We only
// keep track of the length here.
//
// private final char value[];
private final int count;
/** Cache the hash code for the string */
private int hash; // Default to 0
....
}
注释中我们可以看到,已经把字符存储放到runtime里面去了。而且内部修改字符串,也不再是简单的new String出来。
public String replace(char oldChar, char newChar) {
String replaced = this;
if (oldChar != newChar) {
for (int i = 0; i < count; ++i) {
if (charAt(i) == oldChar) {
if (replaced == this) {
replaced = StringFactory.newStringFromString(this);
}
replaced.setCharAt(i, newChar);
}
}
}
return replaced;
}
采用了StringFactory.newStringFromString(),这个是native方法。上述文章中提到了利用反射修改不可访问的value,从而使不可变String,变成可变String。貌似在1.8中已经被封杀了。
String类不可变的好处:
- 字符串池实现的前提。在运行时节约heap空间。不同字符串变量都指向同一个字符串。
- 保障安全。直接影响:账号,密码等都以字符串的形式传递;隐形影响:HashSet,存储String内容,如果可变,破坏键值的唯一性。
- 多线程安全。
- 缓存Hashcode,高效。String不可变,hashcode就是一个定值。不需要重新计算,所以作为map中的键,处理速度会更快。