`
TonyLian
  • 浏览: 401607 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

【第24条】需要时使用保护性拷贝

阅读更多

    Java受欢迎的一个重要原因是它是一门安全的语言。它对于缓冲区溢出、数组越界、非法指针以及其他内存破坏错误自动免疫。

 

    但是,这并不是说你可以高枕无忧,正如前面【第5条】中所述的,某些情况下你还是要自行回收过期引用的。现在我们再来说一下你不得不做的“自我防卫”性工作。

 

    【第5条】中的回收过期引用,即使你没有这么做,顶多是浪费一些内存资源。但是,如果本条所述的“自我防卫”工作你没有到位的话,那后果就可能是灾难性的了,而其错误所在往往不容易被发现。

 

     如果一个方法或构造函数允许可变对象进/出,那么就要考虑一下使用者是否有可能改变它。如果是的话,那你必须对该对象进行保护性拷贝,使进入方法内部的对象是外部时的拷贝而不它本身(因为外部的对象有可能还会被改变)。

 

public class Stuff {
    private String name;
    private Date birthday;

    public Stuff(String name, Date birthday) {
        this.name = name;
        this.birthday = birthday;
    }

    public Date getBrithday() {
        return this.birthday;
    }
}

 

这样的一个职员类,它包括姓名和生日。在没有进行保护性拷贝的保护之前,我们来看看攻击者(攻击者太极端了,很肯能是一个“高级”程序员)是如何开始他的破坏工作的:

 

Date day1 =  new Date(1970,1,1)
Stuff zhangSan = new Stuff("张三", day1);
// .......

// 几行其他代码之后,老先生有想起 day1 来了
day1.setYear(2009);

// 这下坏了,张三先生成了婴儿了,zhangSan.birthday = 2009-1-1

 

这样的原因就是Date型是一个可变类型(Java早期遗留下来的遗憾之一,详见【第13条】)。我们使用保护性拷贝来防之:

 

    public Stuff(String name, Date birthday) {
        this.name = name;
        this.birthday = new Date(birthday.getTime());   // 内部的birthday实际上在这里新创建的一个“日期值”等于实参的新实例
     // 因为,Date型是可变类型,所以“值”虽相等,也是新实例
    }

 

现在这位老先生无论怎么折腾 day1 也不会影响到 张三 大哥的生辰八字了。但是,人家老先生还有高招:

 

zhangSan.getBirthday().setYear(2010);
// 这回张三先生就更惨了,直接回娘胎了

 

如果getBrithday方法是我们必须提供的,那该怎么办呢?答案还是保护性拷贝:

 

    public Date getBrithday() {
        return (Date) this.birthday.clone();  // 我造个新的实例扔给你,你随便折腾吧,影响不到我
    }

 看到了吧,这就是保护性拷贝,使用构造函数也好,克隆也罢,总之是得到一个原对象的副本。

 

    最后要提一下包装类模式(包括适配器模式和装饰模式),根据包装类的本质特征,使用者只需直接访问被包装的对象,就可以破坏包装类的约束条件,但是,这样做的前提是确保不会伤害使用者自己。

 

 

 

【Effective Java 学习笔记】系列连载专题请见:
http://tonylian.iteye.com/categories/64208

 

5
0
分享到:
评论
1 楼 lord_is_layuping 2012-09-25  
copy on write就是一般不会复制,你改一个元素就修改一个,其它共享
http://en.wikipedia.org/wiki/Copy-on-write

如果不是immutable,可能经常要进行保护性拷贝
http://tonylian.iteye.com/blog/391256,保护性拷贝

保护性拷贝直接double了内存的占用
而且,如果在循环中进行保护性拷贝,那就不得了了

scala在后面折腾这些都是以性能为代价的。当然JVM本身也折腾,scala是进一步折腾 :-)

相关推荐

    计算机软件使用许可合同(一).doc

    第二条指出,被许可方可以在特定期限内免费使用软件,但需支付一定的手续费。这表明软件并非永久免费,可能有一定的使用期限。 第三条提到许可程序和许可资料的交付方式,即通过互联网下载,同时被许可方有权向许...

    61条编程经验

    第二条指出类的使用者应该依赖于类的公有接口,但类本身不应该依赖于其使用者。这遵循了依赖倒置原则,提高了代码的可重用性和灵活性。 ### 3. 精简接口 第三条建议减少类的协议中的消息数量,这意味着类的接口...

    网络安全管理制度(6).doc

    系统安全管理 第二十四条 禁止下载互联网上任何未经确认其安全性的软件,严禁使用盗版软件及游戏软件。 第二十五条 外来软盘或光盘须在未联网的单机上检查病毒,确认无毒后方可上网使用。私自使用造 成病毒侵害要...

    LINUX 24学时教程

    第二部分 学习Linux操作系统的基本命令 第4学时 阅读与浏览命令 46 4.1 使用man命令获取帮助 46 4.2 浏览及搜索文件系统 48 4.2.1 使用cd命令进入不同的目录 48 4.2.2 使用pwd命令查看自己所在的位置 48 4.2.3 使用...

    安全审计管理制度.docx

    第二条,信息安全管理部门需定期进行安全审计,审计周期根据审计内容的具体需求来设定,以确保持续性监控和评估。 第三条,管理部门需要规划审计工作流程,包括如何组织审计活动以及如何处理审计发现的问题,确保...

    Java代码编写30条建议

    - 当需要具体实现时,可以考虑使用抽象类来提供部分默认实现。 #### 14. 代码的可维护性 - 保持代码结构清晰,避免代码重复。 - 遵循单一职责原则,每个类只负责单一的功能。 - 使用重构工具定期对代码进行重构,...

    visio教程PDF

    第二部分 创建工程 第4章 使用样板创建新工程 4.1 从样板绘图开始 4.2 Block Diagram文件夹中的样板 4.2.1 Basic Diagram 4.2.2 Block Diagram with Perspective 4.2.3 Block Diagram 4.3 Flow...

    Visio 2000 技术大全

    第二部分 创建工程 第4章 使用样板创建新工程 4.1 从样板绘图开始 4.2 Block Diagram文件夹中的样板 4.2.1 Basic Diagram 4.2.2 Block Diagram with Perspective 4.2.3 Block Diagram 4.3 ...

    61条Java面向对象设计的经验原则.

    #### 原则二十四:减少协作量 - **原则**:尽量减少类和协作者之间的协作量。 - **解释**:减少类和协作者之间传递的不同消息的数量有助于降低系统的复杂度,提高模块间的独立性。 #### 原则二十五:减少扇出 - **...

    视频监控存储方案.doc

    S2600产品作为面向中低端市场的第三代存储产品,其主要特点是提供多种主机端口(FC/iSCSI/SAS/FC-iSCSI)、结合备份软件的磁盘快照功能、BBU掉电保护、SAS和SATA分级存储、硬盘休眠、快照、卷拷贝、远程复制、WORM...

    常见主板报警问题

    3. **3短声**:系统基本内存(第1个64Kb)检查失败,需要更换内存。 4. **4短声**:系统时钟出错,需要维修或更换主板。 5. **5短声**:CPU错误,可能是CPU插座或主板其他部分的问题。 6. **6短声**:键盘控制器错误...

    Reversing:逆向工程揭密

    第二类是从没有源代码的程序出发,生成对应的源程序、系统结构以及相关设计原理和算法思想的文档等,亦即本书重点讨论的二进制逆向工程。 本书共有13章和三个附录,涵盖了逆向工程的基础知识、应用、开发和拓展的...

    软件开发合同范文.doc

    ##### 第二条 定单编号 - **定单编号**:用于唯一标识本次合作的具体订单信息。 ##### 第三条 开发周期 - **开发周期**:指从合同签订之日起到软件开发完成并交付试用版所需的时间。 - **时间范围**:具体天数需要...

Global site tag (gtag.js) - Google Analytics