Effective Java:使可变性最小化

1.什么是不可变类

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

2.使类不可变的原则

  1. 不要提供任何修改对象状态的方法。
  2. 保证类不会被继承。
  3. 使所有的域都是final类型的。
  4. 使所有的域都是私有类型的。
  5. 确保对于任何可变组件的互斥性。 即,确保在该类的外部不会获取到该类中可变对象的引用。比如下面这个例子:
public class MyObject{
  private final List<String> list = new ArrayList<>;//可变对象
  public List<String> getList() {
    return new ArrayList(list);
  }
  public void setList(List<String> list) {
    this.list = new ArrayList(list);
  }
}

3.不可变类的优点

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

4.不可变类的缺点

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

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类。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值