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.

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,766评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,957评论 19 139
  • 这个系列面试题主要目的是帮助你拿轻松到offer,同时还能开个好价钱。只要能够搞明白这个系列的绝大多数题目,在面试...
    独念白阅读 352评论 0 3
  • 1、一个".java"源文件中是否可以包括多个类(不是内部类)?有什么限制?答:可以有多个类,但只能有一个publ...
    岳小川阅读 961评论 0 2
  • 今天的晨读文章是介绍日本销量No.1 的奢侈品店店长写的《聪明女人背小包》,这对做为一个恨不得拎麻包袋上班...
    一只想开悟的猪阅读 154评论 1 1