跟我学(Effective Java 2)第15条:使可变性最小化

第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也指向原始实例中的同一个内部数组。

不可变对象为其他对象提供了大量的构件。

不可变对象真正唯一的缺点是,对于不同的值都需要一个单独的对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值