关于Clone

关于Clone

一般情况下,如果使用clone()方法,则需满足以下条件。
1、对任何对象o,都有o.clone() != o。换言之,克隆对象与原型对象不是同一个对象。
2、对任何对象o,都有o.clone().getClass() == o.getClass()。换言之,克隆对象与原型对象的类型一样。
3、如果对象o的equals()方法定义恰当,则o.clone().equals(o)应当成立。

我们在设计自定义类的clone()方法时,应当遵守这3个条件。一般来说,这3个条件中的前2个是必需的,第3个是可选的。

在这里插入图片描述

浅拷贝

super.clone()方法直接从堆内存中以二进制流的方式进行复制,重新分配一个内存块,因此其效率很高。
由于super.clone()方法基于内存复制,因此不会调用对象的构造函数,也就是不需要经历初始化过程。
只有基本类型的参数会被拷贝一份,非基本类型的对象不会被拷贝一份,而是继续使用传递引用的方式,原型对象与克隆对象的该属性只是指向同一对象的引用,即浅拷贝

	@Override
	public Object clone() throws CloneNotSupportedException{
		Employees employees = (Employees)super.clone();
		return employees;
	}

在这里插入图片描述

深拷贝

在日常开发中,使用super.clone()方法并不能满足所有需求。如类中存在引用对象属性。就要需要实现深拷贝,必须要自己手动修改 clone 方法才行。

	@Override
	public Object clone() throws CloneNotSupportedException{
			List<String> temp = new ArrayList<String>();
			for(String s : this.getEmpList()){
				temp.add(s);
			}
			return new Employees(temp);
	}

在这里插入图片描述

使用序列化(Serializable)

不过如果当原型对象维护很多引用属性的时候,手动分配会比较烦琐。因此,在Java中,如果想完成原型对象的深克隆,则通常使用 序列化(Serializable)的方式。

public class Employees implements Cloneable,Serializable{

	private List<String> empList;
	
	public Employees(){
		empList = new ArrayList<String>();
	}
	
	public Employees(List<String> list){
		this.empList=list;
	}
	
	public void loadData(){
		//read all employees from database and put into the list
		empList.add("Pankaj");
		empList.add("Raj");
		empList.add("David");
		empList.add("Lisa");
	}
	
	public List<String> getEmpList() {
		return empList;
	}

	@Override
	public Object clone() throws CloneNotSupportedException{
			List<String> temp = new ArrayList<String>();
			for(String s : this.getEmpList()){
				temp.add(s);
			}
			return new Employees(temp);
	}
	
	public Object deepClone() throws CloneNotSupportedException{
		try {
			ByteArrayOutputStream bos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(bos);
			oos.writeObject(this);
			
			ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
			ObjectInputStream ois = new ObjectInputStream(bis);
			return (Employees)ois.readObject();
			
		} catch (IOException | ClassNotFoundException e) {
			e.printStackTrace();
			return null;
		}
	}
}

测试

	public static void main(String[] args) throws CloneNotSupportedException {
		Employees emps = new Employees();
		emps.loadData();

		Employees empsNew = (Employees) emps.deepClone();
		Employees empsNew1 = (Employees) emps.deepClone();
		List<String> list = empsNew.getEmpList();
		list.add("John");
		List<String> list1 = empsNew1.getEmpList();
		list1.remove("Pankaj");
		
		System.out.println("emps List: "+emps.getEmpList());
		System.out.println("empsNew List: "+list);
		System.out.println("empsNew1 List: "+list1);
	}

emps List: [Pankaj, Raj, David, Lisa]
empsNew List: [Pankaj, Raj, David, Lisa, John]
empsNew1 List: [Raj, David, Lisa]

还原克隆破坏单例的事故现场
假设有这样一个场景,如果复制的目标对象恰好是单例对象,那会不会使单例对象被破坏呢?
当然,我们在已知的情况下肯定不会这么干,但如果发生了意外怎么办?不妨来试一下

public class A11_EagerInitializedSingletonClone implements Cloneable {

	private static final A11_EagerInitializedSingletonClone instance = new A11_EagerInitializedSingletonClone();

	// private constructor to avoid client applications to use constructor
	private A11_EagerInitializedSingletonClone() {
	}

	public static A11_EagerInitializedSingletonClone getInstance() {
		return instance;
	}
	
	@Override
	public A11_EagerInitializedSingletonClone clone() throws CloneNotSupportedException{
		A11_EagerInitializedSingletonClone employees = (A11_EagerInitializedSingletonClone)super.clone();
		return employees;
	}
}
A11_EagerInitializedSingletonClone instanceOne = A11_EagerInitializedSingletonClone.getInstance();
A11_EagerInitializedSingletonClone instanceOne2  = instanceOne.clone();
System.out.println(instanceOne==instanceOne2);

结果为false确实创建了两个不同的对象。

实际上防止复制破坏单例对象的解决思路非常简单,禁止复制便可。要么我们的单例类不实现Cloneable接口,要么我们重写clone()方法,在clone()方法中返回单例对象即可,具体代码如下

	@Override
	public A11_EagerInitializedSingletonClone clone() throws CloneNotSupportedException{
		return instance;
	}
A11_EagerInitializedSingletonClone instanceOne = A11_EagerInitializedSingletonClone.getInstance();
A11_EagerInitializedSingletonClone instanceOne2  = instanceOne.clone();
System.out.println(instanceOne==instanceOne2);

结果为true

你可能感兴趣的:(基础知识,java)