大话设计模式-Java实现(9)-原型模式

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

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

//简历类
public class Resume1 implements Cloneable {
	
	private String name;
	private String sex;
	private int age;
	private String timeArea;
	private String company;

	public Resume1(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 Resume1("not support clone");
		}
	}
	
}

下面是主程序:

//主程序代码
public class Test1 {

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

运行结果如下:
在这里插入图片描述
现在对工作经历进行调整,转化成一个工作经历类,包含工作时间和工作公司:

public class WorkExperience1 {
	
	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 Resume2 implements Cloneable {
	
	private String name;
	private String sex;
	private int age;

	// 调用workExperience对象
	WorkExperience workExperience;

	public Resume2(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 Resume2("not support clone");
		}
	}

}

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

//主程序代码
public class Test2 {

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

运行结果如下:
在这里插入图片描述
是不是很奇怪,为什么三份简历的工作经历都变成了最后一份简历的?但是个人信息却和之前clone的结果一样?
继续修改测试类:

//主代码部分
public class Test3 {

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

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

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

运行结果如下:
在这里插入图片描述
发现每一个clone出来的简历对象都是一个新的对象,但是每一份的工作经历对象,都采用的是同一个对象。
这就涉及到了浅复制与深复制的概念:

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

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

//工作经历类实现Cloneable接口
public class WorkExperience2 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 WorkExperience2();
		}
	}
	
	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;
	}
	
}
public class Resume3 implements Cloneable {
	
	private String name;
	private String sex;
	private int age;

	protected WorkExperience2 workExperience;

	public Resume3(String name) {
		this.name = name;
		this.workExperience = new WorkExperience2();
	}

	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 {
			Resume3 obj = (Resume3) super.clone();
			obj.workExperience = (WorkExperience2) this.workExperience.clone();
			return obj;
		} catch (CloneNotSupportedException e) {
			System.out.println("clone error:" + e.getMessage());
			return new Resume3("error");
		}
	}

}

下面看主程序代码部分:

//主程序代码
public class Test4 {

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

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

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值