Java大话设计模式学习总结(九)---原型模式

原型模式(Prototype),用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。
来自大话设计模式
Java当中,提供了Cloneable接口,实现了这个接口表示这个类支持被复制。如果一个类没有实现这个接口但是调用了clone()方法,Java编译器将抛出一个CloneNotSupportedException异常。

举例:
有一份简历,需要被复制成多份,发给不同的人,并且需要对简历的部分内容进行调整。

// 简历类
public class Resume implements Cloneable {
    private String name;
    private String sex;
    private int age;
    private String timeArea;
    private String company;
    
    public Resume(String name) {
        this.name = name;
    }
    
    // 设置个人信息
    public void setPersonalInfo(String sex,int age){
        this.sex = sex;
        this.age = age;
    }
    
    // 设置工作经历
    public void setWorkExperience(String timeArea,String  company){
        this.timeArea = timeArea;
        this.company= company;
    }
    
    // 展示
    public void show(){
        System.out.printf("%s %s %s", name, sex, age);
        System.out.println();
        System.out.printf("工作经历 %s %s", timeArea, company);
        System.out.println();
    }
    
    // 调用super.clone()方法,可以实现对该类的复制
    public Object clone(){
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return new Resume("not support clone");
        }
    }
}

下面是主程序:

public class Test {

    public static void main(String[] args) {
        Resume a = new Resume("大鸟");
        a.setPersonalInfo("男", 24);
        a.setWorkExperience("1998-2000", "XX公司");
        
        Resume b = (Resume) a.clone();
        b.setWorkExperience("2000-2006", "YY公司");
        
        Resume c = (Resume) a.clone();
        c.setPersonalInfo("男", 29);
        
        a.show();
        b.show();
        c.show();
    }
}

运行结果如下:

大鸟 男 24
工作经历 1998-2000 XX公司
大鸟 男 24
工作经历 2000-2006 YY公司
大鸟 男 29
工作经历 1998-2000 XX公司

现在对工作经历进行调整,转化成一个工作经历类,包含工作时间和工作公司:

public class WorkExperience {
    private String timeArea;
    private String company;

    public String getTimeArea() {
        return timeArea;
    }

    public void setTimeArea(String timeArea) {
        this.timeArea = timeArea;
    }

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }
}

此时的简历类也做了调整,工作经历部分引用WorkExperience对象

public class Resume implements Cloneable {
    private String name;
    private String sex;
    private int age;
    
    // 调用workExperience对象
    private WorkExperience workExperience;
    
    public Resume(String name) {
        this.name = name;
        workExperience = new WorkExperience();
    }
    
    public void setPersonalInfo(String sex,int age){
        this.sex = sex;
        this.age = age;
    }
    
    // 设置工作经历
    public void setWorkExperience(String timeArea,String  company){
        workExperience.setTimeArea(timeArea);
        workExperience.setCompany(company);
    }
    
    public void show(){
        System.out.printf("%s %s %s", name, sex, age);
        System.out.printf("   工作经历 %s %s", workExperience.getTimeArea(), workExperience.getCompany());
        System.out.println();
    }
    
    public Object clone(){
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return new Resume("not support clone");
        }
    }
}

主程序没有任何变化,为了方便展示结果,显示的时候将个人信息和工作经历显示在了一行,下面是运行结果:

public class Test {

    public static void main(String[] args) {
        Resume a = new Resume("大鸟");
        a.setPersonalInfo("男", 24);
        a.setWorkExperience("1998-2000", "XX公司");
        
        Resume b = (Resume) a.clone();
        b.setWorkExperience("2000-2006", "YY公司");
        
        Resume c = (Resume) a.clone();
        c.setPersonalInfo("男", 29);
        c.setWorkExperience("2006-2010", "ZZ公司");
        
        a.show();
        b.show();
        c.show();
    }
}
// 运行结果
大鸟 男 24   工作经历 2006-2010 ZZ公司
大鸟 男 24   工作经历 2006-2010 ZZ公司
大鸟 男 29   工作经历 2006-2010 ZZ公司

是不是很奇怪,为什么三份简历的工作经历都变成了最后一份简历的?但是个人信息却和之前clone的结果一样?
继续修改测试类:

public class Test {

    public static void main(String[] args) {
        Resume a = new Resume("大鸟");
        a.setPersonalInfo("男", 24);
        a.setWorkExperience("1998-2000", "XX公司");

        Resume b = (Resume) a.clone();
        b.setWorkExperience("2000-2006", "YY公司");

        Resume c = (Resume) a.clone();
        c.setPersonalInfo("男", 29);
        c.setWorkExperience("2006-2010", "ZZ公司");

        a.show();
        b.show();
        c.show();

        System.out.println("a简历和b简历的内存地址一样吗:" + (a == b));
        System.out.println("b简历和c简历的内存地址一样吗:" + (b == c));
        System.out.println("a简历和c简历的内存地址一样吗:" + (a == c));

        System.out.println("a工作经历和b工作经历的内存地址一样吗:" + (a.workExperience == b.workExperience));
        System.out.println("b工作经历和c工作经历的内存地址一样吗:" + (b.workExperience == c.workExperience));
        System.out.println("a工作经历和c工作经历的内存地址一样吗:" + (a.workExperience == c.workExperience));
    }
}

// 结果如下
大鸟 男 24   工作经历 2006-2010 ZZ公司
大鸟 男 24   工作经历 2006-2010 ZZ公司
大鸟 男 29   工作经历 2006-2010 ZZ公司
a简历和b简历的内存地址一样吗:false
b简历和c简历的内存地址一样吗:false
a简历和c简历的内存地址一样吗:false
a工作经历和b工作经历的内存地址一样吗:true
b工作经历和c工作经历的内存地址一样吗:true
a工作经历和c工作经历的内存地址一样吗:true

发现每一个clone出来的简历对象都是一个新的对象,但是每一份的工作经历对象,都采用的是同一个对象。
这就涉及到了浅复制与深复制的概念:

  1. 浅复制:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用都仍然指向原来的对象。
    就是说,每份简历操作的workExperience对象其实都是第一个简历对象中的工作经历对象。
  2. 深复制:把引用对象的变量指向复制过来的新对象,而不是原有的被引用对象。

怎么实现深复制呢?看代码:

// 工作经历类实现Cloneable接口
public class WorkExperience implements Cloneable{
    private String timeArea;
    private String company;

  // 采用clone方法复制,将生成新的对象
    public Object clone(){
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println("clone error:" + e.getMessage());
            return new WorkExperience();
        }
    }
    
    // get/set方法省略
}

public class Resume implements Cloneable {
    private String name;
    private String sex;
    private int age;

    protected WorkExperience workExperience;

    public Resume(String name) {
        this.name = name;
        this.workExperience = new WorkExperience();
    }

    public void setPersonalInfo(String sex, int age) {
        this.sex = sex;
        this.age = age;
    }

    public void setWorkExperience(String timeArea, String company) {
        workExperience.setTimeArea(timeArea);
        workExperience.setCompany(company);
    }

    public void show() {
        System.out.printf("%s %s %s", name, sex, age);
        System.out.printf("   工作经历 %s %s", workExperience.getTimeArea(), workExperience.getCompany());
        System.out.println();
    }

   // 调整原先的clone方法,将workExperience属性也进行复制操作
    public Object clone() {
        try {
            Resume obj = (Resume) super.clone();
            obj.workExperience = (WorkExperience) this.workExperience.clone();
            return obj;
        } catch (CloneNotSupportedException e) {
            System.out.println("clone error:" + e.getMessage());
            return new Resume("error");
        }
    }
}

下面看主程序运行结果:

public class Test {

    public static void main(String[] args) {
        Resume a = new Resume("大鸟");
        a.setPersonalInfo("男", 24);
        a.setWorkExperience("1998-2000", "XX公司");

        Resume b = (Resume) a.clone();
        b.setWorkExperience("2000-2006", "YY公司");

        Resume c = (Resume) a.clone();
        c.setPersonalInfo("男", 29);
       //  c.setWorkExperience("2006-2010", "ZZ公司");

        a.show();
        b.show();
        c.show();

        System.out.println("a工作经历和b工作经历的内存地址一样吗:" + (a.workExperience == b.workExperience));
        System.out.println("b工作经历和c工作经历的内存地址一样吗:" + (b.workExperience == c.workExperience));
        System.out.println("a工作经历和c工作经历的内存地址一样吗:" + (a.workExperience == c.workExperience));
    }
}

// 运行结果
大鸟 男 24   工作经历 1998-2000 XX公司
大鸟 男 24   工作经历 2000-2006 YY公司
大鸟 男 29   工作经历 1998-2000 XX公司
a工作经历和b工作经历的内存地址一样吗:false
b工作经历和c工作经历的内存地址一样吗:false
a工作经历和c工作经历的内存地址一样吗:false

专门注释掉修改c简历的工作经历方法,就是为了更直观的查看工作经历是否都指向相同的内存地址,可以看到,现在各个简历中的工作经历已经和预期一致,而且a简历和c简历中,虽然工作经历相同,但是该对象已经是clone出来的一个新的对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值