第15条:使可变性最小化
不可变类只是其实例不能被修改的类。每个实例中包含的所有信息都必须在创建该实例的时候就提供,并在对象的整个生命周期(lifetime)内固定不变。Java平台包含的不可变类:String、基本类型的包装类、BigInteger、BigDecimal。不可变类比可变类更容易设计、实现和使用。
为了使类成为不可变,要遵循以下五条规则:
1、不要提供任何可以改变成员变量值的方法;
2、保证类不会被扩展。 防止粗心或恶意子类假装对象状态被改变。一般可以使类称为final的,用它修饰类,就不能被继承了;
3、使所有的域都是final的。把成员变量和自己的方法,都使用 final 来修饰,保证它们不能被修改;
4、使所有的域都成为私有的。 防止客户端获得访问被域引用的可变对象的权限,防止客户端直接修改这些对象,从技术上讲,允许不可变的类具有公有的final域,只要这些域包含基本类型的值或者指向不可变对象的引用,但不建议这样做,因为会导致无法再以后的版本改变内部表示法;
5、确保对于任何可变组件的互斥访问。如果类具有指向可变对象的域,则必须确保该类的客户端无法获得指向这些对象的引用,永远不要用客户端提供的对象引用来初始化这样的域,也不要从任何访问方法中返回该对象的引用。在构造器、访问方法和readObject方法中使用保护性拷贝。
/**
* 15 使可变性 最小化
*/
//也可以 不用 final 修饰表达 则需要把构造方法私有化,并提供静态工厂 创建对象
public class Complex {
/*
*不可变类的优势:易于设计,实现和使用,不易出错,安全性高
* 如:String,基本类型的包装类,BigInteger和BigDecimal等。
*
* 设计原则 1. 不要提供任何会修改对象状态的方法
* 2. 保证类不会被扩展
* 3. 使用的所有域都是final的
* 4. 使用的所有域都成为私有的
* 5. 确保对于任何可变组件的互斥访问。 不太理解
*
* 不可变对象的本质是线程安全的,所以他们不要求同步。不可变的对象也可以共享他们的内部信息,
* 不可变的对象为其他对象提供了大量的构建
*
* 缺点,每个不同的值 都需要一个单独的对象,当值过多时,尤其出现循环生成不同的值时 极占内存 影响性能 甚至出现内存泄漏问题。
* */
private final double re;
private final double im;
private static final Complex ZERO = new Complex(0,0);
private static final Complex ONE = new Complex(1,0);
private static final Complex I = new Complex(0,1);
//私有化的构造方法 不能被外部调用
private Complex(double re, double im) {
this.re = re;
this.im = im;
}
//相当于get方法
public double realPart(){
return re;
}
public double imaginaryPart(){
return im;
}
public Complex add(Complex c){
return new Complex(re + c.re , im + c.im);
}
public Complex subtract(Complex c){
return new Complex(re - c.re , im - c.im);
}
public Complex multiply(Complex c){
return new Complex(re * c.re - im * c.im, re * c.im + im * c.re);
}
public Complex divide(Complex c){
double tmp = c.re * c.re + c.im * c.im;
return new Complex((re * c.re + im * c.im) / tmp ,(im * c.re - re * c.im) / tmp);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
//if (!(o instanceof Complex)) return false; 上下两种方式均可。
Complex complex = (Complex) o;
if (Double.compare(complex.re, re) != 0) return false;
return Double.compare(complex.im, im) == 0;
}
@Override
public int hashCode() {
int result;
long temp;
temp = Double.doubleToLongBits(re);
result = (int) (temp ^ (temp >>> 32));
temp = Double.doubleToLongBits(im);
result = 31 * result + (int) (temp ^ (temp >>> 32));
return result;
}
@Override
public String toString() {
return "Complex{" +
"re=" + re +
", im=" + im +
'}';
}
//上一个方式 是 类名final化 确保 不能被继承。提供公有的静态工厂方法 用于生产对象。
public static Complex valueOf(double re, double im){
return new Complex(re,im);
}
}
不可变对象本质上是线程安全的,不要求同步。因为状态无法被修改,所以多线程并发访问这样的对象不会破坏它的不可变性。客户端应该尽可能地重用现有的实例。
不可变对象也可以共享它们的内部信息。如BigInteger类的内部使用了符号数值表示法。符号用一个int类型的值signum表示,数值则用一个int数组mag表示。它有一个negate方法产生一个新的BigInteger,其中数值是一样的,符号是相反的,它不需要拷贝数组,新建的BigInteger也指向原始实例中的同一个内部数组。
不可变对象为其他对象提供了大量的构件。
不可变对象真正唯一的缺点是,对于不同的值都需要一个单独的对象。