Hey 不变类
The secret of success is constancy to purpose. – 成功的秘诀在于对目标的忠实.
读 Effective Java 之 不可变类.
不可变类只是其实例不能被修改的类,每个实例中包含的信息都必须在创建该实例的时候就提供,并在对象的整个生命周期内固定不变.
为什么使用不可变类呢? 不可变类比可变类更易于设计,实现和使用,它们不易出错,并且更加安全.
我们现在熟知的不可变类有 String ,基本的类型的包装类,BigInteger ,BigDecimal.
想要让类变为不可变的,需要遵循5个原则:
1 不要提供任何会修改对象状态的方法;
2 保证类不会背拓展.
3 是所有的域都是final的.
4 是所有的域都变为私有的.
5 确保对任何可变组建的互斥访问.
举个例子: 实现复数之间的加减乘除操作.
public final class Complex {
// 不可变属性
private final double re;
private final double im;
public Complex(double re, double im) {
this.re = re;
this.im = im;
}
// 方法得到 re
public double realPart(){
return re;
}
public double imaginaryPart(){
return im;
}
// 复数的 相减操作 并没有修改之前的,而是创建了新的实例
public Complex subComple(Complex complex){
return new Complex(re-complex.re,im-complex.re);
}
// 复数的相加
public Complex addComple(Complex complex){
return new Complex(re+complex.re,im+complex.im);
}
// 复数的 相乘
public Complex multiComple(Complex complex){
return new Complex(re*complex.re-im*complex.im,re*complex.re+im*complex.im);
}
// 复数的相除
public Complex divideComple(Complex complex){
double tmp = re*complex.re+complex.im*im;
return new Complex((re*complex.re+im*complex.im)/tmp,(im*complex.re-re*complex.im)/tmp);
}
// 重写了 equals 方法
@Override
public boolean equals(Object obj) {
if(obj == this){
return true;
}
if(!(obj instanceof Complex))
return false;
Complex complex = ((Complex) obj);
return Double.compare(re,complex.re)==0&&Double.compare(im,complex.im)==0;
}
// 重写了 hashCode
/**
* 之所以使用 31, 是因为他是一个奇素数.
* 如果乘数是偶数,并且乘法溢出的话,信息就会丢失,
* 因为与2相乘等价于移位运算(低位补0).
* 使用素数的好处并不很明显,但是习惯上使用素数来计算散列结果.
* 31 有个很好的性能,即用移位和减法来代替乘法,
* 可以得到更好的性能: 31 * i == (i << 5) - i,
* 现代的 VM 可以自动完成这种优化.这个公式可以很简单的推导出来.
* @return
*/
@Override
public int hashCode() {
int result = 17+ hashDouble(re);
result = 31 * result +hashDouble(im);
return result;
}
private int hashDouble(double val){
long longBits = Double.doubleToLongBits(val);
return ((int) (longBits ^ (longBits >>> 32)));
}
@Override
public String toString() {
return "Complex{" +
"re=" + re +
", im=" + im +
'}';
}
}
这样就是类变成了不可变的,不过也有着一些缺点,就是不同的值,都有一个独立的对象,所以,根据自己的需求使用吧.