第三十九条:必要时进行保护性拷贝

使Java使用起来如此舒适的一个因素在于,它是一门安全的语言。这意味着,它对于缓冲区溢出,数组越界,非法指针以及其他的内存破坏

都自动免疫,而这些错误却困扰着诸如C和C++这样的不安全语言。在一门安全的语言中,在设计类的时候,可以确切的知道,无论系统的

其他部分发生什么事情,这些类的约束都可以保持为真。对于那些“把所有的内存当成一个巨大的数组来看待”的语言来说,这是不可能的。

 

有一种情况进行保护性拷贝就是需要的:

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类并不是一个不可变类。

如果客户端这样使用我们定义的Period类 :

Date  startTime   =  new Date();

Date  endTime   = new  Date();

Period  per =  new  Period(startTime ,  endTime );

endTime.setYear(78);

本来我们希望Period是一个不可变类,只有初始化的那一种状态,但是由于Date类是一个可变类,我们获取了final  Date  startTime域所引用

的那个对象,而这个对象是一个Date类,我们就可以改变这个对象的状态,从而导致Period的实例对象中的状态进行了改变。

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

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 !”);

    }

}

这样在使用上面那样的客户端代码时,在对endTime对象改变状态时,就不会影响到Period类的实例对象状态了。

但是如果客户端使用这样的代码   per.start().setYear(78);   这样同样破坏了per对象的状态。

那么对于返回实例对象域的start(),end()方法也应该使用保护性拷贝。

public  Date   start()

{

      return   new  Date(startTime.getTime());

}

public  Date  end()

{

      return  new   Date(endTime.getTime());

}

这样,我们定义的Period类就真正变成了一个不可变类,它的实例对象只有在初始化的那一种状态。

 

使用保护性拷贝是为了我们的代码更加健壮,考虑客户端代码的种种恶意破坏。

一般情况,当我们想要定义一个不可变类,但是这个类的域的类型确实一个可变类,那么我们在定义构造器的时候是需要进行保护性拷贝的。

而且记住对于参数有效性的检验应该在保护性拷贝之后。

posted @ 2015-05-07 14:25  wangliyue  阅读(466)  评论(0编辑  收藏  举报