字符串不可变性
String对象一旦创建,内容不能被修改。
不可变性概念
什么是不可变
字符串对象创建后,其内容不可更改,任何修改操作都会产生新对象。
Java
String str = "Hello";
str = str + " World"; // 看似修改,实际创建新对象
// 原字符串"Hello"仍在内存中
// 新字符串"Hello World"是新创建的对象
不可变性的本质
String类内部数组是final的,不可修改。
Java
// String类源码(简化)
public final class String {
private final char[] value; // final修饰,不可变
// 没有提供修改value数组的方法
}
不可变性验证
看似修改实则新建
Java
String s1 = "Hello";
String s2 = s1.concat(" World"); // 拼接操作
System.out.println(s1); // "Hello"(原字符串未变)
System.out.println(s2); // "Hello World"(新字符串)
// s1和s2是不同的对象
System.out.println(s1 == s2); // false
所有操作返回新对象
Java
String str = "Hello";
// 每个操作都返回新字符串
String upper = str.toUpperCase(); // "HELLO"(新对象)
String sub = str.substring(1); // "ello"(新对象)
String rep = str.replace('H', 'J'); // "Jello"(新对象)
// 原字符串不变
System.out.println(str); // "Hello"
不可变性的好处
安全性
字符串不可被篡改,适合存储敏感信息。
Java
// 作为参数传递,不会被修改
public void process(String param) {
// param内容安全,不会被意外修改
}
// 多线程共享安全
String shared = "配置信息"; // 多线程访问无需加锁
字符串常量池优化
不可变才能安全共享,实现常量池。
Java
String s1 = "Java";
String s2 = "Java";
// s1和s2共享同一对象,节省内存
// 如果可变,s1修改会影响s2
HashMap键的稳定性
字符串作为HashMap键,hashCode稳定。
Java
Map<String, Object> map = new HashMap<>();
map.put("key", value);
// 字符串不可变,hashCode不变
// 如果可变,key内容改变后hashCode改变,无法找到value
安全传递
字符串传递给其他方法或类时不会被修改。
Java
String url = "http://example.com";
Connection conn = connect(url);
// url不会被connect方法修改
不可变性的代价
拼接产生大量对象
Java
// 低效:每次拼接创建新对象
String result = "";
for (int i = 0; i < 1000; i++) {
result = result + i; // 创建1000个临时对象
}
// 高效:使用StringBuilder
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 1000; i++) {
sb.append(i); // 不创建新对象
}
String result = sb.toString();
内存占用
大量字符串操作会产生临时对象,增加GC压力。
Java
// 临时对象示例
String a = "A";
String b = "B";
String c = a + b; // 创建临时StringBuilder,再创建String
// 产生额外对象
不可变性的实现机制
final类和final字段
Java
// String是final类,不能继承
public final class String {
// value数组是final,不可替换
private final char[] value;
// 没有setter方法
// 所有修改方法返回新对象
}
私有字段无修改方法
Java
// value数组私有,外部无法访问
// 没有提供修改value内容的方法
// concat、substring等方法内部创建新数组
不可变性相关面试题
为什么String设计为不可变?
- 安全性:防止意外修改,适合存储敏感信息
- 常量池:支持字符串常量池共享
- 线程安全:多线程访问无需同步
- hashCode稳定:适合作为HashMap键
String能否真正修改?
技术上可以通过反射修改,但不推荐。
Java
// 反射修改(不推荐,破坏安全性)
String str = "Hello";
Field field = String.class.getDeclaredField("value");
field.setAccessible(true);
char[] chars = (char[]) field.get(str);
chars[0] = 'J'; // 修改底层数组
// 实际变为"Jello",但这是危险操作
常见误解
误解1:str = str + "x"是修改
Java
String str = "Hello";
str = str + "World";
// 不是修改原对象
// 是创建新对象,str指向新对象
// 原对象"Hello"仍在内存中(可能被GC回收)
误解2:substring修改原字符串
Java
String str = "Hello World";
String sub = str.substring(0, 5);
System.out.println(str); // "Hello World"(未变)
System.out.println(sub); // "Hello"(新对象)
要点总结
- String不可变:创建后内容不能修改
- 所有修改操作返回新对象
- 底层final char[]保证不可变
- String类是final类,不能继承
- 好处:安全、常量池共享、线程安全、hashCode稳定
- 代价:频繁拼接产生大量临时对象
- 拼接大量字符串用StringBuilder
- 作为HashMap键稳定可靠
- 多线程访问无需同步
- 反射可修改但不推荐
📝 发现内容有误?点击此处直接编辑