String 为什么是不可变的?
String 为什么是不可变的?
String
类中使用 final
关键字修饰字符数组来保存字符串,所以String
对象是不可变的。
1 | public final class String implements java.io.Serializable, Comparable<String>, CharSequence { |
🐛 修正:我们知道被
final
关键字修饰的类不能被继承,修饰的方法不能被重写,修饰的变量是基本数据类型则值不能改变,修饰的变量是引用类型则不能再指向其他对象。因此,final
关键字修饰的数组保存字符串并不是String
不可变的根本原因,因为这个数组保存的字符串是可变的(final
修饰引用类型变量的情况)。
String
真正不可变有下面几点原因:1. 保存字符串的数组被
final
修饰且为私有的,并且String
类没有提供/暴露修改这个字符串的方法。
2.String
类被final
修饰导致其不能被继承,进而避免了子类破坏String
不可变。相关阅读:如何理解 String 类型值的不可变? - 知乎提问open in new window
在 Java 9 之后,
String
、StringBuilder
与StringBuffer
的实现改用byte
数组存储字符串。
1 | public final class String implements java.io.Serializable,Comparable<String>, CharSequence { |
Java 9 为何要将
String
的底层实现由char[]
改成了byte[]
?新版的 String 其实支持两个编码方案:Latin-1 和 UTF-16。如果字符串中包含的汉字没有超过 Latin-1 可表示范围内的字符,那就会使用 Latin-1 作为编码方案。Latin-1 编码方案下,
byte
占一个字节(8 位),char
占用 2 个字节(16),byte
相较char
节省一半的内存空间。
String不可变的意义
String
类在 Java 中是一个非常重要的类,它表示字符序列,并且设计为不可变的(immutable)。String
类底层使用 final char[] value
数组来存储字符数据,这意味着一旦创建了一个 String
对象,它的内容就不能再被更改。不可变性带来了多个好处:
- 线程安全:
- 不可变性保证了
String
对象在多线程环境中是安全的,因为它的内容不会改变。这意味着多个线程可以共享一个String
对象,而不用担心数据一致性问题。
- 节省内存:
- Java 运行时会在字符串常量池中存储字符串字面量,如果存在相同的字符串字面量,就会共享相同的内存空间。这种共享减少了重复字符串的存储,节省了内存资源。例如,
String s1 = "abc"; String s2 = "abc";
在这种情况下,s1
和s2
引用的是同一个对象。
- 简化编程模型:
- 不可变性简化了编程模型,因为一旦创建了
String
对象,就不需要担心后续的操作会改变它的内容。这有助于减少程序中的错误。
- 哈希码稳定性:
String
类重写了hashCode
方法,使其返回值依赖于字符串的内容。由于String
是不可变的,其哈希码在对象创建时计算一次即可,之后不会改变。这对于HashMap
或其他依赖哈希码的数据结构非常重要,因为哈希码的变化会导致这些数据结构无法正常工作。
- 方便作为键使用:
- 由于
String
的不可变性,它可以安全地作为HashMap
或其他集合框架中的键使用。当String
作为键时,其内容不会改变,因此总是可以正确地找到对应的值。
- 简化内部优化:
- JVM 能够对不可变的
String
做更多的优化,因为它们的内容永远不会改变。例如,JIT 编译器可以更自由地缓存结果,提高性能。
- 简化序列化过程:
- 由于
String
对象是不可变的,它们在序列化和反序列化过程中不会出现问题,因为对象的状态不会发生变化。