设计模式 | 三、原型模式(浅克隆、深克隆)[PrototypePattern]

原型模式

源码:https://github.com/GiraffePeng/design-patterns

1、定义

原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象。

2、应用场景

原型模式主要适用于以下场景:

  • 1、类初始化消耗资源较多。
  • 2、new 产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
  • 3、构造函数比较复杂。
  • 4、循环体中生产大量对象时。

在 Spring 中,原型模式应用得非常广泛,例如 scope=“prototype”。在我们经常用的 JSON.parseObject()也是一种原型模式。

3、浅克隆

针对接口编程,让我们先创建一个克隆接口PrototypeInteface.java

public interface PrototypeInteface {
	public PrototypeInteface clone();
}

创建一个实体类实现PrototypeInteface接口,重写clone方法。

public class ShallowPrototype implements PrototypeInteface{

	private int id;
	
	private String name;
	
	private String type;
	
	private List<String> arr;
	
	public List<String> getArr() {
		return arr;
	}

	public void setArr(List<String> arr) {
		this.arr = arr;
	}

	public ShallowPrototype() {
		super();
	}

	public ShallowPrototype(int id, String name, String type,List<String> arr) {
		super();
		this.id = id;
		this.name = name;
		this.type = type;
		this.arr = arr;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}
	
	@Override
	public ShallowPrototype clone() {
		ShallowPrototype shallowPrototype = new ShallowPrototype();
		shallowPrototype.setId(this.id);
		shallowPrototype.setName(this.name);
		shallowPrototype.setType(this.type);
		shallowPrototype.setArr(this.arr);
		return shallowPrototype;
	}
}

创建测试类:

public class PrototypeTest {

	public static void main(String[] args) {
		ShallowPrototype shallowPrototype = new ShallowPrototype(1, "name", "type",new ArrayList<String>() {{
			add("测试");
		}});
		ShallowPrototype clone = shallowPrototype.clone();
		System.out.println(shallowPrototype.getName() == clone.getName());
		System.out.println(shallowPrototype.getArr() == clone.getArr());
		clone.getArr().add("修改");
		List<String> arr = shallowPrototype.getArr();
		for (String string : arr) {
			System.out.println(string);
		}
	}
}

执行后打印的控制台结果:

true
true
测试
修改

从测试结果看出 arr 集合的引用地址是相同的,意味着复制的不是值,而是引用的地址。这样的话,如果我们修改任意一个对象中的引用数据类型,shallowPrototype 和clone 的 arr 值(List)都会改变。这就是我们常说的浅克隆。只是完整复制了值的数据,没有复制引用对象本身去创建新的引用对象。换言之,所有的引用对象仍然指向原来的对象。

JDK中也默认提供了Object的clone方法,只需在要求可以克隆的类上实现Cloneable接口,写出clone方法,调用super.clone即可实现浅克隆,例如:

//实现Cloneable接口
public class PrototypeClone implements Cloneable{

	private int id;
	
	private String name;
	
	private String type;
	
	private List<String> arr;

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public List<String> getArr() {
		return arr;
	}

	public void setArr(List<String> arr) {
		this.arr = arr;
	}

	public PrototypeClone(int id, String name, String type, List<String> arr) {
		super();
		this.id = id;
		this.name = name;
		this.type = type;
		this.arr = arr;
	}
	
	public PrototypeClone clone() {
		try {
			//调用super.clone方法
			return (PrototypeClone) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return null;
	}
}

创建测试类:

public class TestJdkClone {

	public static void main(String[] args) {
		PrototypeClone prototypeClone = new PrototypeClone(1, "123", "1", new ArrayList<String>() {{add("ceshi");}});
		PrototypeClone clone = prototypeClone.clone();
		System.out.println(prototypeClone.getArr() == clone.getArr());
	}
}

打印结果:

true

说明JDK提供的clone也为浅克隆。

4、深克隆

深克隆就是仅仅克隆一个对象的值,而不克隆其引用地址,相当于重新在堆内存中开辟一块空间分配个该对象中的属性。
实现方式我们可以采用序列化与反序列化来实现。
同样创建接口:

public interface DeepCloneInterface {
	public Object cloneObject();
}

创建实现类,实现DeepCloneInterface 以及 Serializable接口(为了反序列化)

public class DeepPrototype implements DeepCloneInterface,Serializable{
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	private int id;
	
	private String name;
	
	private List<String> arr;
	
	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public List<String> getArr() {
		return arr;
	}

	public void setArr(List<String> arr) {
		this.arr = arr;
	}
	 
	//序列化与反序列化实现深克隆
	@Override
	public Object cloneObject() {
		try {
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(bos);
			
			oos.writeObject(this);
			ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
			ObjectInputStream ois = new ObjectInputStream(bis);
			Object readObject = ois.readObject();
			
			ois.close();
			bis.close();
			oos.flush();
			oos.close();
			bos.close();
			return readObject;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

}

测试类:

public class DeepTest {

	public static void main(String[] args) {
		DeepPrototype deepPrototype = new DeepPrototype();
		deepPrototype.setArr(new ArrayList<String>() {{add("测试");}});
		deepPrototype.setId(1);
		deepPrototype.setName("测试");
		
		
		DeepPrototype cloneObject = (DeepPrototype)deepPrototype.cloneObject();
		
		System.out.println(cloneObject.getArr() == deepPrototype.getArr());
	}
}

打印结果:

false

可以看到深克隆下,克隆的仅仅为值,并不会去克隆引用类型的分配地址。

5、克隆破坏单例模式

如果我们克隆的目标的对象是单例对象,那意味着,深克隆就会破坏单例。实际上防止克隆破坏单例解决思路非常简单,禁止深克隆便可。要么单例类不实现Cloneable 接口;要么我们重写 clone()方法,在 clone 方法中返回单例对象即可,具体如下:

....
@Override
protected Object clone() {
	return 具体的单例实例对象;
}
....
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值