原型模式

在讲述这个模式之前,我们先看一个案例:复制简历

先创建一个简历类

public class Resume {
    private String name;
    private String sex;
    private String age;
    private String timeArea;
    private String company;
    
    public Resume(String name) {
        super();
        this.name = name;
    }
    
    public void setPersonalInfo(String sex,String age){
        this.sex = sex;
        this.age = age;
    }

    public void setWorkExperience(String timeArea, String company) {
        this.timeArea = timeArea;
        this.company = company;
    }

    public void display() {
        System.out.println("Resume [name=" + name + ", sex=" + sex + ", age=" + age + ", timeArea=" + timeArea + ", company="
                + company + "]");
    }
}

测试方法

public class Test {
    public static void main(String[] args) {
        Resume a = new Resume("张三");
        a.setPersonalInfo("男", "30");        
        a.setWorkExperience("1999-2000", "XX公司");
        
        Resume b = new Resume("张三");
        b.setPersonalInfo("男", "30");    
        b.setWorkExperience("1999-2000", "XX公司");
        
        Resume c = new Resume("张三");
        c.setPersonalInfo("男", "30");        
        c.setWorkExperience("1999-2000", "XX公司");
        
        a.display();
        b.display();
        c.display();
    }
}

输出结果

Resume [name=张三, sex=男, age=30, timeArea=1999-2000, company=XX公司]
Resume [name=张三, sex=男, age=30, timeArea=1999-2000, company=XX公司]
Resume [name=张三, sex=男, age=30, timeArea=1999-2000, company=XX公司]

上面的实现非常简单,如果需要20分简历,就需要实例化20次,如果想把1999改成1998,那么也需要修改20次。

看到这肯定觉得这样的操作非常笨,只要把不变的信息定义成全局变量,定义一个List集合,利用循环直接实例化20次,每循环一次,把全局变量的信息赋值给新对象,再把新对象放入List集合中,最后List集合里就有了20个相同的简历对象。

这么做当然和上面的代码产生了相同的效果,但这是真正的复制吗?

如果已知一个对象有200个成员变量,我们要复制这个对象,那么就需要把这个对象的200个成员变量先取出来,用全局变量存储这200个成员变量的值,然后再创建一个新对象,把全局变量存储的值赋值给新对象的200个成员变量。期间还要小心谨慎,不能赋错了。这样做不是一样的傻么。

那怎样复制一个对象呢?

原型模式介绍:http://www.runoob.com/design-pattern/prototype-pattern.html

简历类实现 Cloneable,重写 clone()

public class Resume implements Cloneable {
    private String name;
    private String sex;
    private String age;
    private String timeArea;
    private String company;
    
    public Resume(String name) {
        super();
        this.name = name;
    }
    
    public void setPersonalInfo(String sex,String age){
        this.sex = sex;
        this.age = age;
    }

    public void setWorkExperience(String timeArea, String company) {
        this.timeArea = timeArea;
        this.company = company;
    }

    public void display() {
        System.out.println("Resume [name=" + name + ", sex=" + sex + ", age=" + age + ", timeArea=" + timeArea + ", company="
                + company + "]");
    }
    
    public Resume clone() throws CloneNotSupportedException{
        Resume o = (Resume) super.clone();
        return o;
    }
}

测试方法

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Resume a = new Resume("张三");
        a.setPersonalInfo("男", "30");    
        a.setWorkExperience("1999-2000", "XX公司");
        
        Resume b = a.clone();
        a.display();
        b.display();
    }
}

输出结果

Resume [name=张三, sex=男, age=30, timeArea=1999-2000, company=XX公司]
Resume [name=张三, sex=男, age=30, timeArea=1999-2000, company=XX公司]

这样就实现了对象的复制,调用对象的clone()就能复制出一个新的对象。

克隆相比之前的new对象更快吗?

每new一次,都需要执行一次构造函数,如果构造函数的执行时间很长,那么多次的执行这个初始化操作就实在是太低效了。一般在初始化的信息不发生变化的情况下,克隆是最好的办法。这既隐藏了对象创建的细节,又对性能是大大对的提高。

如果复制的对象中包含其他自定义的类,那么这些自定义的类也会被复制吗?

创建一个地址类

public class Address {
    private String country;
    private String province;
    
    public Address(String country, String province) {
        super();
        this.country = country;
        this.province = province;
    }

    @Override
    public String toString() {
        return "Address [country=" + country + ", province=" + province + "]";
    }
    //省略getter和setter方法   
}

在简历类中增加一个地址类成员变量

public class Resume implements Cloneable {
    private String name;
    private String sex;
    private String age;
    private String timeArea;
    private String company;
    private Address address;
    
    public Resume(String name) {
        super();
        this.name = name;
    }
    
    public void setPersonalInfo(String sex,String age){
        this.sex = sex;
        this.age = age;
    }

    public void setWorkExperience(String timeArea, String company) {
        this.timeArea = timeArea;
        this.company = company;
    }
    
    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public void display() {
        System.out.println("Resume [name=" + name + ", sex=" + sex + ", age=" + age + ", timeArea=" + timeArea + ", company="
                + company + ", address=" + address + "]");
    }
    
    public Resume clone() throws CloneNotSupportedException{
        Resume o = (Resume) super.clone();
        return o;
    }
}

测试方法

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("中国", "山东");
        
        Resume a = new Resume("张三");
        a.setPersonalInfo("男", "30");    
        a.setWorkExperience("1999-2000", "XX公司");
        a.setAddress(address);
        
        Resume b = a.clone();
        b.setPersonalInfo("男", "31");    
        a.getAddress().setProvince("江苏");
        b.getAddress().setProvince("湖南");
        
        a.display();
        b.display();
    }
}

输出结果

Resume [name=张三, sex=男, age=30, timeArea=1999-2000, company=XX公司, address=Address [country=中国, province=湖南]]
Resume [name=张三, sex=男, age=31, timeArea=1999-2000, company=XX公司, address=Address [country=中国, province=湖南]]

这和我们期望的不同。我们想要简历a的省份是江苏,简历b的省份是湖南,结果却是两份简历的省份都是湖南。

为什么是这样?

如果复制的字段能通过他们的内容判断是否相等,那么复制时就会逐位复制。如:基本数据类型和String类。否则只会复制引用但不复制引用的对象。这就叫做“浅复制”,被复制对象的所有变量都含有与原来的对象相同的值,而所有的引用变量都仍然指向原来的对象。

那如果想把要引用变量所引用的对象也复制,该怎么办?

这种方式叫做“深复制”。

让地址类也实现 Cloneable,重写 clone()

public class Address implements Cloneable{
    private String country;
    private String province;
    
    public Address(String country, String province) {
        super();
        this.country = country;
        this.province = province;
    }
    public Address clone() throws CloneNotSupportedException{
        Address o = (Address) super.clone();
        return o;
    }
    @Override
    public String toString() {
        return "Address [country=" + country + ", province=" + province + "]";
    }

    //省略getter和setter方法
}

简历类在克隆时也将地址类克隆

public class Resume implements Cloneable {
    private String name;
    private String sex;
    private String age;
    private String timeArea;
    private String company;
    private Address address;
    
    public Resume(String name) {
        super();
        this.name = name;
    }
    
    public void setPersonalInfo(String sex,String age){
        this.sex = sex;
        this.age = age;
    }

    public void setWorkExperience(String timeArea, String company) {
        this.timeArea = timeArea;
        this.company = company;
    }
    
    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public void display() {
        System.out.println("Resume [name=" + name + ", sex=" + sex + ", age=" + age + ", timeArea=" + timeArea + ", company="
                + company + ", address=" + address + "]");
    }
    
    public Resume clone() throws CloneNotSupportedException{
        Resume o = (Resume) super.clone();
        this.address = this.address.clone();
       return o;
    }
}

测试方法

public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address address = new Address("中国", "山东");
        
        Resume a = new Resume("张三");
        a.setPersonalInfo("男", "30");    
        a.setWorkExperience("1999-2000", "XX公司");
        a.setAddress(address);
        
        Resume b = a.clone();
        b.setPersonalInfo("男", "31");    
        a.getAddress().setProvince("江苏");
        b.getAddress().setProvince("湖南");
        
        a.display();
        b.display();
    }
}

输出结果

Resume [name=张三, sex=男, age=30, timeArea=1999-2000, company=XX公司, address=Address [country=中国, province=江苏]]
Resume [name=张三, sex=男, age=31, timeArea=1999-2000, company=XX公司, address=Address [country=中国, province=湖南]]

这样就实现了复制引用变量时,把引用变量指向的对象也复制一遍。

原文地址:https://www.cnblogs.com/jwen1994/p/10001545.html