Item 15: Minimize mutability

不可变类是其实例不能被修改的类
比如String、基本数据类型的包装类(String的基本数据类型就是String)、BigIntegerBigDecimal

要使一个类变成不可变的,要遵守下面的规则:

  1. 不提供任何修改对象状态的方法。比如setter getter中的setter(也称作mutator)就不能提供。

  2. 保证类不被扩展。通常用finla class实现(也可以用private构造函数的方法,也就是静态工厂方法里提到的)。

  3. 所有的fileds都设成私有的、final的。

4、 Ensure exclusive access(互斥访问) to any mutable components.

下面贴一段很长的代码,是一个复数运算的类:

public final class Complex {
    private final double re;
    private final double im;

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

    // Accessors with no corresponding mutators
    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 (o == this)
            return true;
        if (!(o instanceof Complex))
            return false;
        Complex c = (Complex) o;
// See page 43 to find out why we use compare instead of ==
        return Double.compare(re, c.re) == 0 &&
                Double.compare(im, c.im) == 0;
    }

    @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(re);
        return (int) (longBits ^ (longBits >>> 32));
    }

    @Override
    public String toString() {
        return "(" + re + " + " + im + "i)";
    }
}

可以看到这个类符合了上面列出来的要求。有个特点,每次计算完成之后都new一个实例,而不是修改传进来的。不可变类可以只有一种状态,就是创建时的状态。

不可变类天生就是线程安全的,不要求同步。多个线程并发访问的时候不会遭到破坏。所以可以被自由地共享。所以你根本不需要给不可变类提供拷贝构造器

坚决不要为每个getter都配置一个setter。除非有很好的理由让类变成可变的,不然他就应该是不可变的。

缺点

不可变类唯一的缺点是,对于每一个类不同的值都需要一个但对的对象。比如要创建几百万个BigInteger

BONUS:

我一直疑惑为什么String可以用=来初始化,而不用new;后来想了想,因为String是符合类型呀,它的基本数据类型和包装数据类型都是String。
引用:

String password="ok";利用到了字符串缓冲池,也就是说如果缓冲池中已经存在了相同的字符串,就不会产生新的对象,而直接返回缓冲池中的字符串对象的引用。
如:
String a = "ok";
String b = "ok";
String c = new String("ok");
String d = new String("ok");
System.out.println(a==b);//将输出"true";因为两个变量指向同一个对象。
System.out.println(c==d);//将输出"flase";因为两个变量不指向同一个对象。虽然值相同,只有用c.equals(d)才能返回true.

你可能感兴趣的:(Item 15: Minimize mutability)