跟我学(Effective Java 2)第39条:必要时进行保护性拷贝

第39条:必要时进行保护性拷贝

使Java使用起来如此舒适的一个因素在于,它是一门安全的语言(对于缓冲区溢出,数组越界,非法指针等内存破坏行为免疫)。但即使在安全语言中,如果不采取一些措施,还是无法使自己与其他的类隔离开。假设类的客户会尽一切手段来破坏这个类的约束条件,在这样的前提下,你必须保护性地设计程序。

/**
 * 39 :必要时进行保护性拷贝
 * 如果类具有从客户端得到或者返回到客户端的可变组件,类就必须保护性的拷贝这些组件。如果拷贝的成本受到限制,并且类信任他的客户端不会进行修改,或者恰当的修改,那么就需要在文档中指明客户端调用者的责任(不的修改或者如何有效修改)。
 * 特别是当你的可变组件的生命周期很长,或者会多层传递时,隐藏的问题往往暴漏出来就很可怕。
 * 基于这种形式,最好使用构造器或者静态工厂模式
 */
public final class Period {
    private final Date start;
    private final Date end;
    public Period(Date start, Date end) {
        //没有进行复制保护
        /*if(start.compareTo(end) > 0){
            throw new IllegalArgumentException(start + " after " + end);
        }
        this.start = start;
        this.end = end;*/
        //Tue Apr 30 16:11:09 CST 2019
        //Sun Apr 30 16:11:09 CST 1978
        //------------------------------------------
        //进行了复制保护
        this.start = new Date(start.getTime());
        this.end = new Date(end.getTime());
        if(start.compareTo(end) > 0){
            throw new IllegalArgumentException(start + " after " + end);
        }
        //Tue Apr 30 16:14:44 CST 2019
        //Tue Apr 30 16:14:44 CST 2019
    }

    public Date start(){
        //return start; 二次攻击时 对方生成不可信实例,访问本方法达到篡改意图
        return new Date(start.getTime());
    }

    public Date end(){
        //return end; 二次攻击时 对方生成不可信实例,访问本方法达到篡改意图
        return new Date(end.getTime());
    }
    //这里修改了final的值 并没有报错因为Date类本身是可变的。
    public static void main(String []args){
        Date start = new Date();
        Date end = new Date();
        Period period = new Period(start, end);
        System.out.println(period.end());
        //end.setYear(78);
        period.end().setYear(78);//二次攻击 通过访问参数方法达到修改目的
        System.out.println(period.end());

    }
}

保护性拷贝是在检查参数的有效性之前进行的,并且有效性检查是针对拷贝之后的对象,而不是针对原始的对象。这样做可以避免在危险阶段从另一个线程改变类的参数。

同时也请注意,因为Date是非final的,不能保证clone方法返回的就是Date对象,它有可能返回一个专门出于恶意目的而设计的不可信子类的实例。对于参数类型可以被不可信任方子类化的参数,请不要用clone方法进行保护性拷贝。

总之,如果类具有从客户端得到或者返回客户端的可变组件,类就必须保护性地拷贝这些组件。如果拷贝成本受到限制,并且类信任调用它的客户端不会修改内部组件,就可以指名客户端的职责是不得修改收到影响的组件,以此来代替保护性拷贝。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值