一、String 不可变的定义
1. 不可变对象的定义
- 不可变对象:一旦创建,其状态(属性值)不能被修改。
- String 的不可变性:String 对象一旦创建,其字符序列不能被修改。
二、String 不可变的源码解析
1. String 类的定义
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
// 存储字符序列的数组
private final char value[];
// 缓存字符串的哈希值
private int hash; // Default to 0
// 其他字段和方法...
}
关键点:
final类:String 类不能被继承。final char value[]:存储字符序列的数组是final的,引用不可变。
2. 字符串的创建
public String(String original) {
this.value = original.value;
this.hash = original.hash;
}
关键点:
- 新创建的 String 对象直接引用原字符串的
value数组,而不是复制一份。
3. 字符串的修改操作
substring 方法
public String substring(int beginIndex) {
if (beginIndex < 0) {
throw new StringIndexOutOfBoundsException(beginIndex);
}
int subLen = value.length - beginIndex;
if (subLen < 0) {
throw new StringIndexOutOfBoundsException(subLen);
}
return (beginIndex == 0) ? this : new String(value, beginIndex, subLen);
}
关键点:
- 如果
beginIndex为 0,直接返回原字符串(引用相同)。 - 否则,创建一个新的 String 对象。
concat 方法
public String concat(String str) {
int otherLen = str.length();
if (otherLen == 0) {
return this;
}
int len = value.length;
char buf[] = Arrays.copyOf(value, len + otherLen);
str.getChars(buf, len);
return new String(buf, true);
}
关键点:
- 创建一个新的字符数组
buf,复制原字符串和新字符串的内容。 - 返回一个新的 String 对象。
4. 字符串的哈希值缓存
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}
关键点:
- 哈希值在第一次计算后被缓存,因为字符串不可变,哈希值不会改变。
三、String 不可变的好处
1. 安全性
- 不可变对象是线程安全的:多个线程可以共享不可变对象,无需同步。
- 防止篡改:字符串作为参数传递时,不会被意外修改。
2. 性能优化
- 哈希值缓存:字符串的哈希值只需计算一次,适合作为哈希表的键。
- 字符串池:不可变性使得字符串池(String Pool)成为可能,节省内存。
3. 设计简化
- 简化 API 设计:不可变对象的行为更可预测,减少错误。
四、String 不可变的实现原理
1. final 关键字
- 类:
final修饰的类不能被继承,防止子类破坏不可变性。 - 字段:
final修饰的字段引用不可变,防止被重新赋值。
2. 私有字段
- value 数组:
private final char value[],外部无法直接访问或修改。
3. 防御性拷贝
- 构造函数:在创建新字符串时,复制字符数组,防止外部修改。
五、代码示例
1. 字符串的不可变性
String str1 = "Hello";
String str2 = str1.concat(" World");
System.out.println(str1); // 输出:Hello
System.out.println(str2); // 输出:Hello World
解释:
str1的值没有被修改,concat方法返回了一个新的字符串。
2. 字符串池
String str3 = "Hello";
String str4 = "Hello";
System.out.println(str3 == str4); // 输出:true
解释:
str3和str4引用字符串池中的同一个对象。
六、Summary
-
String 不可变的原因:
final类和字段。- 私有字符数组。
- 防御性拷贝。
-
不可变的好处:
- 线程安全。
- 性能优化(哈希值缓存、字符串池)。
- 设计简化。
通过理解 String 不可变的源码和设计原理,开发者可以更好地利用字符串的特性,编写高效、安全的代码。