zoukankan      html  css  js  c++  java
  • 第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(end) > 0)
                throw new IllegalArgumentException(start + " after " + end);
            this.start = start;
            this.end = end;
        }
        
        public Date start() {
            return start;
        }
        
        public Date end() {
            return end;
        }
    }

    因为Date本身是可变的,因此很容易违反起始时间不能在结束时间之后的约束。

    Date start = new Date();
    Date end = new Date();
    Period p = new Period(start, end);
    end.setYear(78);//改变了终止时间,破坏类的约束条件

    为保护Period实例的内部信息被破坏,对构造器的每个可变参数进行保护性拷贝,使用备份对象作为Period实例的组件,而不使用客户端传入的参数。

    public Period(Date start, Date end) {
        this.start = new Date(start.getTime());
        this.end = new Date(end.getTime());
        if(this.start.compareTo(this.end) > 0)
            throw new IllegalArgumentException(start + " after " + end);
    }

    保护性拷贝在参数有效性检查之前,这样做可以避免在多线程环境中,参数有效性通过后,从另外一个线程改变参数,使得参数有效性的检测失效。

    不使用clone方法进行保护性拷贝的原因是Date是非final的,不能保证clone方法一定返回Date对象,它可能会返回转么处于恶意的不可信子类的实例。

    进行了上面的工作后,改变Period实例仍有可能,因为它的访问方法提供了对其可变内部成员的访问能力:

    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());
    }

    采用新构造器和新访问方法后,Period真正地是不可变的了,不管程序员多么恶意和不合格,都绝对不会违反周期起始时间大于结束时间的约束。访问方法与构造器不同,在进行保护性拷贝的时候允许使用clone方法,因为Period内部Date的对象一定是Date,而不可能是某个不可信的子类。

    只要有可能,应该使用不可变的对象作为对象内部组件,这样不必为保护性拷贝操心。在Period例子中,使用Date.getTime()返回的基本类型作为内部的时间表示法,就不需要进行保护性拷贝了。

    如果类具有从客户端得到或者返回客户端的可变组件,类就必须保护性地拷贝这些组件,如果拷贝成本受到限制,并且类信任调用者不会修改内部组件,不进行保护性拷贝也是可以的,类的文档必须清楚说明这一点。

  • 相关阅读:
    eclipse注释乱码的一个小问题
    《java 编程思想》 读书笔记 (一)
    《java 编程思想》 读书笔记 (一)
    055_SSM——sprinMVC的返回值
    054_jQuary——html()与text()方法的区别?
    053_Servlet——resp.getWrite()与resp.getPrint()的区别?
    052_html——html转环成jsp出现乱码的问题?
    051_html——checkbox的默认值与工作属性
    050_SSM——SpringMVCV中的各个器都起了什么作用?
    047_SSM——为什么返回String使用StringHttpMessageConverter解析时会造成乱码?
  • 原文地址:https://www.cnblogs.com/13jhzeng/p/5743747.html
Copyright © 2011-2022 走看看