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

使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类就真正变成了一个不可变类,它的实例对象只有在初始化的那一种状态。

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

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

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

时间: 2024-10-05 21:55:56

第三十九条:必要时进行保护性拷贝的相关文章

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

Java是一门安全的语言,但是如果不采取措施,还是无法保证安全性.假设类的客户端会尽其所能来破坏类的约束条件,因此必须保护性地设计程序. 考虑下面的类,声称表示一段不可变的时间周期: import java.util.Date; public final class Period { private final Date start; private final Date end; public Period(Date start, Date end) { if(start.compareTo(

必要时进行保护性拷贝

假设类的客户端会尽其所能来破坏这个类的约束条件,因此你必须保护性的设计程序.demo: 1 import java.util.Date; 2 3 public final class Period { 4 private final Date start; 5 private final Date end; 6 public Period(Date start,Date end) { 7 if(start.compareTo(end) > 0){ 8 throw new IllegalArgum

第七章:方法。ITEM39:必要时进行保护性拷贝。

1 package com.twoslow.cha7; 2 3 import java.util.Date; 4 5 public final class Period { 6 7 private final Date start ; 8 private final Date end ; 9 10 public Period(Date start , Date end) { 11 if(start.compareTo(end) > 0) 12 throw new IllegalArgumentE

centos DNS服务搭建 第三十节课

centos  DNS服务搭建     第三十节课 上半节课 下半节课 一. DNS原理相关DNS 为Domain Name System(域名系统)的缩写,它是一种将ip地址转换成对应的主机名或将主机名转换成与之相对应ip地址的一种服务机制.其中通过域名解析出ip地址的叫做正向解析,通过ip地址解析出域名的叫做反向解析. DNS使用TCP和UDP, 端口号都是53, 但它主要使用UDP,服务器之间备份使用TCP.全世界只有13台“根”服务器,1个主根服务器放在美国,其他12台为辅根服务器,DN

程序员的奋斗史(三十八)——大学断代史(二)——我与数据库的故事

文/.温国兵 惰性人皆有之,也算是人的一大天性.几日之前便构思好此文,怎奈每日杂事繁多,今日才提起笔,作下此文.本文谈谈我与数据库的故事. 说起和数据库结缘,还得从大一说起.大一刚开始接触C语言,每日就沉浸在无止境的代码中.在网上查资料的过程中,看到别人用C语言写了一个小型的图书操作程序,数据库采用的是SQL Server,运行出来的效果图很炫,惊叹原来还可以这样管理数据.熟知C语言的同学肯定对文件操作不陌生,当时我们写程序如果有静态数据,都是放在文件里的.直到后来做的C语言课程设计,我也是一大

NeHe OpenGL教程 第三十五课:播放AVI

转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢. NeHe OpenGL第三十五课:播放AVI 在OpenGL中播放AVI: 在OpenGL中如何播放AVI呢?利用Windows的API把每一帧作为纹理绑定到OpenGL中,虽然很慢,但它的效果不错.你可以试试. 首先我得说我非常喜欢这一章节.Jonat

NeHe OpenGL教程 第三十六课:从渲染到纹理

转自[翻译]NeHe OpenGL 教程 前言 声明,此 NeHe OpenGL教程系列文章由51博客yarin翻译(2010-08-19),本博客为转载并稍加整理与修改.对NeHe的OpenGL管线教程的编写,以及yarn的翻译整理表示感谢. NeHe OpenGL第三十六课:从渲染到纹理 放射模糊和渲染到纹理: 如何实现放射状的滤镜效果呢,看上去很难,其实很简单.把渲染得图像作为纹理提取出来,在利用OpenGL本身自带的纹理过滤,就能实现这种效果,不信,你试试. 嗨,我是Dario Corn

三十六、git clone简介

翻译整理自: http://web.mit.edu/~mkgray/project/silk/root/afs/sipb/project/git/git-doc/git-clone.html 在使用git来进行版本控制时,为了得一个项目的拷贝(copy),我们需要知道这个项目仓库的地址(Git URL). Git能在许多协议下使用,所以Git URL可能以ssh://, http(s)://, git://,或是只是以一个用户名(git 会认为这是一个ssh 地址)为前辍. 有些仓库可以通过不只

QT开发(三十五)——QT进程间通信

QT开发(三十五)--QT进程间通信 Qt 是一个跨平台的应用框架,其进程间通信机制当然可以使用所在平台的进程间通信机制,如在Windows平台上的Message机制.共享内存.文件映射.管道.Socket等.其中,Qt对一些许多平台共有的IPC机制进行了封装. 一.TCP/IP 其实就是通过网络模块实现的IPC.不过Qt对其进行了封装,并提供了两个层次的API,包括应用程序级的QNetworkAccessManager, QFtp等和底层的QTcpSocket, QTcpServer, QSs