String为什么设计成不可变的

String为什么设计成不可变的

String 是 Java 中最常用的类之一,它的不可变性(immutability)是 Java 设计的核心决策。本文从多个维度解析这一设计背后的考量。

什么是不可变性

String 对象一旦创建,其内容就不能被修改:

String s = "hello";
s = s + " world"; // 创建了新的String对象,原对象未改变

String 类被声明为 final,且内部字符数组也是 final:

public final class String {
private final char[] value; // JDK 8
private final byte[] value; // JDK 9+,compact strings
}

原因一:字符串常量池

不可变性使得字符串常量池(String Pool)成为可能:

String a = "java";
String b = "java";
System.out.println(a == b); // true,指向常量池同一对象

如果 String 可变,常量池中的字符串被修改会影响所有引用,造成不可预期的行为。

原因二:线程安全

不可变对象天然线程安全,无需同步:

// 多线程环境下安全共享
public static final String CONFIG_KEY = "app.config";

这对于高并发场景下的配置项、常量定义等非常重要。

原因三:HashCode 缓存

String 的 hashCode 在第一次计算后被缓存:

private int hash;  // 默认值0

public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
// 计算hash...
hash = h;
}
return h;
}

这使得 String 作为 HashMap 的 Key 时性能极高,是 HashMap 最常用的键类型。

原因四:安全性

String 广泛用于网络连接、文件路径、权限检查等敏感场景:

// 如果String可变,以下检查可被绕过
void connect(String host) {
if (!host.equals("safe.host.com")) {
throw new SecurityException();
}
// 建立连接...
}

不可变性保证了安全检查后的值不会被篡改。

原因五:性能优化

JDK 9 引入 Compact Strings,使用 byte[] 替代 char[]:

  • LATIN-1 编码(单字节)节省 50% 内存
  • UTF-16 编码(双字节)处理中文等字符

不可变性使得这种内部优化可以安全地进行,而不会影响外部行为。

常见误区

1. StringBuilder vs StringBuffer

// 单线程优先使用StringBuilder
StringBuilder sb = new StringBuilder();
sb.append("hello").append(" world");

// 多线程使用StringBuffer(线程安全,但性能较低)
StringBuffer sbf = new StringBuffer();

2. 字符串拼接性能

// 编译期优化为StringBuilder
String s = "a" + "b" + "c"; // 等效于 "abc"

// 循环中必须使用StringBuilder
String result = "";
for (String item : list) {
result += item; // 每次循环创建新对象!
}

3. intern() 方法

String s1 = new String("hello");
String s2 = s1.intern(); // 放入常量池并返回引用

最佳实践

  1. 比较用 equals:永远不要用 == 比较字符串内容
  2. 拼接用 StringBuilder:循环或大量拼接场景
  3. 适度使用 intern():内存敏感但字符串重复率高时
  4. 巧用 isEmpty() 和 isBlank()(Java 11+)
// Java 11+ 判断空白字符串
if (str.isBlank()) { ... }

// 去除首尾空白(支持Unicode)
String clean = str.strip();

总结

String 的不可变性是 Java 设计的精妙之处,它在安全性、线程安全、性能和功能之间取得了平衡。理解这一设计有助于写出更安全、更高效的代码。


   转载规则


《String为什么设计成不可变的》 小乐 采用 知识共享署名 4.0 国际许可协议 进行许可。
  目录