String为什么设计成不可变的
String 是 Java 中最常用的类之一,它的不可变性(immutability)是 Java 设计的核心决策。本文从多个维度解析这一设计背后的考量。
什么是不可变性
String 对象一旦创建,其内容就不能被修改:
String s = "hello"; |
String 类被声明为 final,且内部字符数组也是 final:
public final class String { |
原因一:字符串常量池
不可变性使得字符串常量池(String Pool)成为可能:
String a = "java"; |
如果 String 可变,常量池中的字符串被修改会影响所有引用,造成不可预期的行为。
原因二:线程安全
不可变对象天然线程安全,无需同步:
// 多线程环境下安全共享 |
这对于高并发场景下的配置项、常量定义等非常重要。
原因三:HashCode 缓存
String 的 hashCode 在第一次计算后被缓存:
private int hash; // 默认值0 |
这使得 String 作为 HashMap 的 Key 时性能极高,是 HashMap 最常用的键类型。
原因四:安全性
String 广泛用于网络连接、文件路径、权限检查等敏感场景:
// 如果String可变,以下检查可被绕过 |
不可变性保证了安全检查后的值不会被篡改。
原因五:性能优化
JDK 9 引入 Compact Strings,使用 byte[] 替代 char[]:
- LATIN-1 编码(单字节)节省 50% 内存
- UTF-16 编码(双字节)处理中文等字符
不可变性使得这种内部优化可以安全地进行,而不会影响外部行为。
常见误区
1. StringBuilder vs StringBuffer
// 单线程优先使用StringBuilder |
2. 字符串拼接性能
// 编译期优化为StringBuilder |
3. intern() 方法
String s1 = new String("hello"); |
最佳实践
- 比较用 equals:永远不要用 == 比较字符串内容
- 拼接用 StringBuilder:循环或大量拼接场景
- 适度使用 intern():内存敏感但字符串重复率高时
- 巧用 isEmpty() 和 isBlank()(Java 11+)
// Java 11+ 判断空白字符串 |
总结
String 的不可变性是 Java 设计的精妙之处,它在安全性、线程安全、性能和功能之间取得了平衡。理解这一设计有助于写出更安全、更高效的代码。