在面试中经常会问到关于不可变类的问题。
问题一
Q:什么是不可变(immutable)类?
A:一个对象在构建后,就不会被修改了,就是不可变类。
问题二
Q:为什么需要(immutable)类?
A:这个问题有这么两种情形,①当在多线程情况下,多个线程对一个变量同时进行访问,会造成逻辑错误,及数据本身逻辑不一致。
public class SynchronizedRGB {
// Values must be between 0 and 255.
private int red;
private int green;
private int blue;
private String name;
private void check(int red,
int green,
int blue) {
if (red < 0 || red > 255
|| green < 0 || green > 255
|| blue < 0 || blue > 255) {
throw new IllegalArgumentException();
}
}
public SynchronizedRGB(int red,
int green,
int blue,
String name) {
check(red, green, blue);
this.red = red;
this.green = green;
this.blue = blue;
this.name = name;
}
public void set(int red,
int green,
int blue,
String name) {
check(red, green, blue);
synchronized (this) {
this.red = red;
this.green = green;
this.blue = blue;
this.name = name;
}
}
public synchronized int getRGB() {
return ((red << 16) | (green << 8) | blue);
}
public synchronized String getName() {
return name;
}
public synchronized void invert() {
red = 255 - red;
green = 255 - green;
blue = 255 - blue;
name = "Inverse of " + name;
}
}
SynchronizedRGB就必须很小心的使用避免出现不一致的状态,假设一个线程执行了如下代码
SynchronizedRGB color =
new SynchronizedRGB(0, 0, 0, "Pitch Black");
...
int myColorInt = color.getRGB(); //Statement 1
String myColorName = color.getName(); //Statement 2
如果另外一个线程执行了color.set在Statement 1之后但在Statement 2之前, myColorInt跟myColorName就不匹配了。
②在HashMap这种散列表中,键的选取,必须是hashcode不会变化。假设如果出现这种情况,如果一个键值对插入了散列表中,后续键的内部属性被修改了。如果修改的内部属性会决定hash值,那么该散列表就紊乱了。所以说散列表的键通常都是String、Integer这两个都是不可变类。
不可变类需要满足的条件
- 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.