概念
官方说明如下:
The following rules define a simple strategy for creating immutable objects. Not all classes documented as "immutable" follow these rules. This does not necessarily mean the creators of these classes were sloppy — they may have good reason for believing that instances of their classes never change after construction. However, such strategies require sophisticated analysis and are not for beginners.
- Don't provide "setter" methods — methods that modify fields or objects referred to by fields.
- Make all fields
final
and private
.- Don't allow subclasses to override methods. The simplest way to do this is to declare the class as
final
. A more sophisticated approach is to make the constructor private
and construct instances in factory methods.- If the instance fields include references to mutable objects, don't allow those objects to be changed:
- Don't provide methods that modify the mutable objects.
- Don't share references to the mutable objects. Never store references to external, mutable objects passed to the constructor; if necessary, create copies, and store references to the copies. Similarly, create copies of your internal mutable objects when necessary to avoid returning the originals in your methods.
Applying this strategy to
SynchronizedRGB
results in the following steps:
- There are two setter methods in this class. The first one,
set
, arbitrarily transforms the object, and has no place in an immutable version of the class. The second one, invert
, can be adapted by having it create a new object instead of modifying the existing one.- All fields are already
private
; they are further qualified as final
.- The class itself is declared
final
.- Only one field refers to an object, and that object is itself immutable. Therefore, no safeguards against changing the state of "contained" mutable objects are necessary.
不可变对象的特点:
- 不要提供 setter 方法 - 修改字段引用的字段或对象的方法;
- 所有字段被 final 和 private 修饰;
- 不允许子类重写方法。最简单的方法是将类声明为 final。更复杂的方法是 private 在工厂方法中构造构造函数和构造实例;
- 如果实例字段包含对可变对象的引用,则不允许更改这些对象:
- 不要提供修改可变对象的方法;
- 不要共享对可变对象的引用。永远不要存储对传递给构造函数的外部可变对象的引用; 如有必要,创建副本并存储对副本的引用。同样,必要时创建内部可变对象的副本,以避免在方法中返回原始对象;
- 线程安全性;
- 会产生大量的“垃圾”;
看这样一个类:
构造这样一个类产生的对象其实并不是不可变对象,因为对象持有的引用依然可以被修改。
String 类的不可变性
Java 中的 String 类就是一个典型的不可变类,也许会有人说 String 类不是有一个 replace() 方法嘛,那 String 还是可以更改啊,可以看看 String 的 replace() 方法:
可以看到最终还是 new 了一个 String。
参考资料:
https://docs.oracle.com/javase/tutorial/essential/concurrency/imstrat.html