Effective Java之必要时进行保护性拷贝(三十九)

我们来看一个不可变对象的攻守问题:

public  class   Period

{

         private   final   Date   startTime;

         private   finale  Date   endTime;

         public   Period(Date  startTime , Date  endTime)

        {

               if(startTime.compareTo(endTime) > 0)

               { throw   new  IllegalArgumentException(“startTime  after  endTime !”);  }

                this.startTime = startTime;

                this.endTime  = endTime;

        }

        pubilc   Date   start()

        {  return   this.startTime ; }

        public   Date  end()

        { return  this.endTime  ; }

}

这个类貌似是一个不可变类 ,因为startTime和endTime域都是final的,但是它并不是一个严格的不可变类,因为Date类并不是一个不可变类,所以Date实例指向内存中的引用地址不可变,但是引用的内容可以变,所以可以这样来攻击不可变类

Date  startTime   =  new Date();

Date  endTime   = new  Date();

Period  per =  new  Period(startTime ,  endTime );

endTime.setYear(78);

这个时候我们对于构造器进行保护性拷贝

public   Period(Date   startTime , Date  endTime )

{

      this.startTime  =  new Date (startTime.getTime());

      this.endTime   =  new  Date(endTime.getTime());

     if(this.startTime.compareTo(this.endTime)  >  0)

    { 

          throw   new    IllegalArgumentException(“startTime  after   endTime !”);

    }

}

也就是说把这个可变的Date引用指向了一个拷贝,这样endTime.setYear(78)永远也修改不了这个拷贝。

值得注意的是:保护性拷贝是在检查参数有效性之前进行的,而且针对的是拷贝对象。为什么呢?因为担心并发情况下检查有效性通过之后,另一个线程改变了可变对象使其有效性错误,但是依然能进行保护性拷贝的错误情况。

同时我们没有用Date的clone方法进行保护性拷贝。为什么呢?
因为Date是非final的,不能保证clone方法返回的就是Date对象,它有可能返回一个专门出于恶意目的而设计的不可信子类的实例。
例如:上面例子中如果别有用心之人新建了一个Mydate继承了Date
那么构造方法中就可以传入MyDate对象,那么调用的就是MyDate的clone方法,那么他就可以在clone方法上动手脚,把实例的引入记录起来,供攻击者访问。

还有一种攻击方法:

Date start = new Date();
Date end = new Date();
Period p = new Period(start, end);
p.end().setYear(78);

原因是它的访问方法提供了对其可变内部成员的访问能力。
解决方法是使它返回内部域的保护性拷贝。

public Date start() {
    return new Date(start.getTime());
}

public Date end() {
    return new Date(end.getTime());
}

总结:参数的保护性拷贝策略不仅仅是针对不可变类,如果类具有从客户端得到或者返回到客户端的可变组件,类必须保护性地拷贝这些组件。如果受到拷贝成本的约束,就应该明确向客户端指明。

避免保护性拷贝的方法是将对象组件设置为不可变组件。或者把客户端和类放在同一个包中,不暴露出去。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值