使可变性最小

第十五条:使可变性最小


1.什么是不可变类?

(1)需要的所有参数必须在实例化的时候都传进去。
(2)对象中所有信息在对象的整个生命周期中都保持不变。

2.使类不可变的原则

(1)不要提供任何修改对象状态的方法。
(2)保证类不会被继承。
(3)使所有的域都是final类型的。
(4)使所有的域都是私有类型的。
(5)确保对于任何可变组件的互斥性。意思就是,确保在该类的外部不会获取到该类中可变对象的引用。比如下面这个例子:

public class MyObject{
  private final List list = new ArrayList<>;//可变对象
  public List getList() {
    return new ArrayList(list);
  }
  public void setList(List list) {
    this.list = new ArrayList(list);
  }
}

3.不可变类的优点

(1)不可变类简单。
不可变类只有一种状态,就是它被创建出来时候的状态,如果你要根据这个类进行一系列复杂操作,那么这个操作无论在什么时候结果都是相同的,所以你可以直接将结果缓存起来,在下一次执行同样操作的时候取出来,而不必再进行下一次操作。
(2)不可变类本质上是线程安全的,它不需要同步锁。
(3)对于不可变类,你永远都不需要实现拷贝方法。拷贝方法对它来说是没有意义的。
(4)不可变类可以被自由的共享。

4.不可变对象的缺点

(1)对于每一个不同的值都需要创建一个新的对象。

5.缺点的弥补办法

(1)先猜测一下经常用到哪些多步骤的操作,然后将它们作为基本数据类型提供。比如Integer,它将值为-128到127的对象缓存起来,但调用valueOf(int i)的时候,直接从缓存中拿,不用再重复创建提高效率。

(2)我们可以创建一个可变配套类。例如String是一个不可变类,它的可变配套类为StirngBuilder和StringBuffer。下面是一个例子,我们现在实现一个复数类,对外提供一个相加的方法如下:

public class Complex {
  private final double re;//实部
  private final double im;//虚部

  private Complex(double re, double im) {
    this.re = re;
    this.im = im;
  }

  public static Complex valueOf(double re, double im) {
    return new Complex(re, im);
  }

  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 class ComplexBuilder {
  private double re;
  private double im;
  
  private ComplexBuider(double re, double im) {
    this.re = re;
    this.im = im;
  }
  public static ComplexBuider newInstance(Complex c) {
    return new ComplexBuilder(c.realPart(), c.imaginaryPart());
  }

  public void add(Complex c) {
    this.re = this.re + c.realPart();
    this.im = this.im + c.imaginaryPart();
  }

  public Complex toComplex() {
    return Complex.valueOf(this.re, this.im);
  }
}

在客户端中我们如果需要用一个复数和另一个复数相加100次,我们如果不用ComplexBuilder的话就像下面这样,算上最开始穿件的两个实例,我们将会创建102个实例:

public class Test {
  @Test
  public void addNoBuiderTest() throws Exception{
    Complex c1 = Complex.valueOf(1, 2);
    Complex c2 = Complex.valueOf(2, 3);
    for (int i = 0 ; i < 100 ; i++) {
      c1 = c1.add(c2);
    }
  }
}

现在改用ComplexBuilder,现在我们只会创建4个实例:

public class Test {
  @Test
  public void addNoBuiderTest() throws Exception{
    Complex c1 = Complex.valueOf(1, 2);
    Complex c2 = Complex.valueOf(2, 3);
    ComplexBuilder cb = ComplexBuider.newInstance(c1);
    for (int i = 0 ; i < 100 ; i++) {
      cb.add(c2);
    }
    c1 = cb.toComplex();
  }
}

6.总结

(1)坚决不要为每个getter都生成setter。
(2)能将类做成不可变的就做成不可变的。
(3)一般比较小的值类都是需要做成不可变的。
(4)对于一些比较大的值类尽量考虑实现成不可变类。
(5)性能方面很有必要的时候才需提供配套类。
(6)如果真的不能作为不可变类,那就尽量限制其可变性
(7)对于一个类的初始化只能在构造器或是静态工厂中完成。也就是说类的初始化操作(赋值之类的)只能执行一次,就是在构造器或是静态工厂中。这一点参考java类库中的TimerTask类。

你可能感兴趣的:(使可变性最小)