String类是不可变类,一个String对象被创建以后,包含这个对象中的字符串序列是不可改变的。与其问String为什么是不可变的,还不如问String类是如何实现其对象不可变的。
不可变性的表现
我们可以通过比较对象的哈希码来进行判断这个对象是否是一个新的对象

反复执行都会是同一个结果
![]()
此时我们对原本的字符串进行一个拼接操作进行修改

执行结果为

我们可以看到其哈希码修改了,也就意味着字符串修改了地址,成为了一个新的对象。
那 String类是如何实现其对象不可变的?
1.String 类是用关键字 final 修饰的
String 类是用关键字 final 修饰的,使得 String 类不可被继承,进而避免了破坏 String 类的不可变。
2.保存字符串的数组被 final 修饰且为私有的
成员变量 value 是用 final 修饰的,保证了 value 的引用地址不可变,但是里面的具体元素是可以被改变的。看下面这个例子:
final int[] value = {1,2};
int[] another = {4,5,6};
value = another; // 编译报错,final 不可变
但是我们是可以修改数组里边的值的
final int[] value = {1,2};
value[1] = 3;
3.String 类没有提供/暴露修改这个字符串的方法
观察String类中的源码,我们发现String这个类没有提供/暴露修改这个字符串的方法。拼接、分割等方法也是返回的是新的字符串或者字符串数组。
总结
根据上述三点,就实现了字符串类型的不可变性。
不可变的好处与坏处
线程安全性(好处)
在并发场景下,多个线程读取同一个资源,是不会引发竟态条件的。只有在对资源进行些操作时才有危险。不可变对象不能被修改,所以是线程安全的。
节省空间,提高效率(好处)

String 还有字符串常量池的属性, one 和 two 两个变量指向的是同一个地址。在大量使用字符串的情况下,可以节省内存空间,提高效率。
String 的不可变条件是必要条件,要是内存啦字符串内容能够改来改去,这么做就完全没有意义。
修改性能不高(坏处)
由于 String 的不可变性,每次对 String 类型进行改变的时候,都会生成一个新的 String 对象,然后将指针指向新的 String 对象。为什么引入 StringBuilder,StringBuffer
StringBuilder 与 StringBuffer 都继承自 AbstractStringBuilder 类,在 AbstractStringBuilder 中也是使用字符数组保存字符串,不过没有使用 final 和 private 关键字修饰,最关键的是这个 AbstractStringBuilder 类还提供了很多修改字符串的操作,如 expandCapacity、append、insert、indexOf 等公共方法。
abstract class AbstractStringBuilder implements Appendable, CharSequence {
char[] value;
// append 方法
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
//...
}
StringBuffer 对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的。StringBuilder 并没有对方法进行加同步锁,所以是非线程安全的。
StringBuffer 每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用 StringBuilder 相比使用 StringBuffer 仅能获得 10%~15% 左右的性能提升,但却要冒多线程不安全的风险。

4251

被折叠的 条评论
为什么被折叠?



