前言
说到不可变对象,想必大部分人都不会陌生。Java程序员在日常开发过程中100%会用到的 String 对象就属于不可变对象吗,今天我们就来聊聊不可变对象这个话题。
什么是不可变对象
《Effective Java》中是这样定义不可变对象的:
不可变对象(Immutable Object):对象一旦被创建后,对象所有的状态及属性在其生命周期内不会发生任何变化。
简单的说,就是一个对象在创建后,不能对该对象进行任何更改。比如下面这段代码:
public class ImmutableObject {
private int value;
public ImmutableObject(int value) {
this.value = value;
}
public int getValue() {
return this.value;
}
}
因为 ImmutableObject 不提供任何 setter 方法,并且成员变量 value 是基本数据类型,getter 方法返回的是 value 的拷贝,所以一旦 ImmutableObject 实例被创建后,该实例的状态无法再进行更改,因此该类具备不可变性。
String类
我们常见的String类,为什么说字符串是不可变的?我的代码中经常改变String的值啊!如下所示:
String string = "ABC";
string = string.concat("DE");
这样,上面的字符串不就改变成"ABCDE"了么?但是实际上,我们得到的已经是一个新的字符串。
程序会在堆中重新创建一个"ABCDE"字符串,和"ABC"并不是同一个对象。
所以,一旦一个string对象在内存(堆)中被创建出来,他就无法被修改。而且,String类的所有方法都没有改变字符串本身的值,都是返回了一个新的对象。
string类如何设计成不可变
-
String 类被 final 关键字修饰,所以它不会有子类,这就意味着没有子类可以重写它的方法,改变它的行为。 -
String 类的数据存储在 char[] 数组中,而这个数组也被 final 关键字修饰了,这就表示 String 对象是没法被修改的,只要初始化一次,值就确定了。
不可变的好处
安全性
String 是 Java 中最基础也是最长使用的类,经常用于存储一些敏感信息,例如用户名、密码、网络连接等。因此,String 类的安全性对于整个应用程序至关重要。
节省空间(字符串常量池)
通过使用常量池,内容相同的字符串可以使用同一个对象,从而节省内存空间。如果 String 是可变的,试想一下,当字符串常量池中的某个字符串对象被很多地方引用时,此时修改了这个对象,则所有引用的地方都会改变,这可能会导致预期之外的情况。
性能
String 被广泛应用于 HashMap、HashSet 等哈希类中,当对这些哈希类进行操作时,例如 HashMap 的 get/put,hashCode 会被频繁调用。
线程安全
String 对象是不可修改的,如果线程尝试修改 String 对象,会创建新的 String,所以不存在并发修改同一个对象的问题。
由于不可变性,String 的 hashCode 只需要计算1次后就可以缓存起来,因此在哈希类中使用 String 对象可以提升性能。
总结
由于字符串是不可变的,因此它们的引用可以被视为普通变量,可以在方法之间和线程之间传递它们,而不必担心它所指向的实际字符串对象是否会改变。
本文由 mdnice 多平台发布