克隆(复制)在Java中是一种常见的操作,目的是快速获取一个对象副本。克隆分为深克隆和浅克隆。

浅克隆:创建一个新对象,新对象的属性和原来对象完全相同,对于非基本类型属性,仍指向原有属性所指向的对象的内存地址。

深克隆:创建一个新对象,属性中引用的其他对象也会被克隆,不再指向原有对象地址。

浅拷贝案例

public class Cat implements Cloneable{
	
	private String name;
	
	private Skill skill;
	
	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @return the skill
	 */
	public Skill getSkill() {
		return skill;
	}

	/**
	 * @param skill the skill to set
	 */
	public void setSkill(Skill skill) {
		this.skill = skill;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	public Object clone() {
		Object clone = null;
		try {
			clone = super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return clone;
	}

}
public class Skill {
	
	private String name = "小猫";
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
	public void onTree() {
		System.out.println(name+"会上树");
	}
	
	public void catchMouse() {
		System.out.println(name+"会抓老鼠");
	}
	
	
}
Cat cat = new Cat();
cat.setName("白猫");

cat.setSkill(new Skill());

Cat catClone = (Cat) cat.clone();

System.out.println("更改浅拷贝非基本类型数据前:"+cat.getSkill().getName());

catClone.getSkill().setName("大猫");

System.out.println("更改浅拷贝非基本类型数据后:"+cat.getSkill().getName());

System.out.println(cat.getName() == catClone.getName());
System.out.println(cat.getSkill() == catClone.getSkill());

结果:

更改浅拷贝非基本类型数据前:小猫
更改浅拷贝非基本类型数据后:大猫
true
true

被克隆的对象和克隆出来的对象 其 对象中的引用对象 Skill , 其指向的是同一地址,这样子的克隆被称之为 浅克隆,拷贝与被拷贝中的非八种基本数据类型的引用对象进行修改,2个对象都受影响。

浅克隆只克隆对象中的基本数据类型,包括 byte、short、int、long、float、double、boolean、char、String (八种基本数据类型和一个引用类型),而对象中的其他非String引用对象类型则不会被克隆

深拷贝案例

我们改造一下上面的浅拷贝:
Skill实现Cloneable接口,并重写clone方法:

public class Skill implements Cloneable{
	
	private String name = "小猫";
	
	public String getName() {
		return name;
	}

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

	public void onTree() {
		System.out.println(name+"会上树");
	}
	
	public void catchMouse() {
		System.out.println(name+"会抓老鼠");
	}
	
	public Skill clone() {
		Skill clone = null;
		try {
			clone = (Skill) super.clone();
			
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return clone;
	}
	
}

Cat类扩展下,使对象的引用也克隆:

public Object clone() {
		Cat clone = null;
		try {
			clone = (Cat) super.clone();
			clone.setSkill(this.getSkill().clone());
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return clone;
	}

运行结果:

更改深拷贝非基本类型数据前:小猫
更改深拷贝非基本类型数据后:小猫
true
false

可以看出,深拷贝改其中一个对象,另一个不受影响。

对象序列化拷贝

如引用类型属性有多层引用类型属性关系,或者有的时候我们的对象中的对象 如果不是那么方便的访问其属性,不能够对其clone方法进行改写的时候,这个时候应该使用字节流加上序列化的方法完成对对象的深克隆。
注意:序列化拷贝即使八种基本类型也是不一样的。

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class Cat implements Cloneable,Serializable{
	
	private String name;
	
	private Skill skill;
	
	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @return the skill
	 */
	public Skill getSkill() {
		return skill;
	}

	/**
	 * @param skill the skill to set
	 */
	public void setSkill(Skill skill) {
		this.skill = skill;
	}

	/**
	 * @param name the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	public Object clone() {

		ByteArrayOutputStream bos = null ;
		ObjectOutputStream oos = null ;
		ByteArrayInputStream bis = null ;
		ObjectInputStream ois  = null ;
		try {
			//序列化
			 bos = new ByteArrayOutputStream();
			 oos = new ObjectOutputStream(bos);
			oos.writeObject(this);
			
			
			//反序列化
			 bis = new ByteArrayInputStream( bos.toByteArray() );
			 ois = new ObjectInputStream( bis );
			 Cat copy = (Cat) ois.readObject();
			
			
			return copy;
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			return null;
		}finally{
			try {
				bos.close();
				oos.close();
				bis.close();
				ois.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}

}
import java.io.Serializable;

public class Skill implements Serializable{
	
	private String name = "小猫";
	
	public String getName() {
		return name;
	}

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

	public void onTree() {
		System.out.println(name+"会上树");
	}
	
	public void catchMouse() {
		System.out.println(name+"会抓老鼠");
	}
	
	
	
}

运行结果:

更改深拷贝非基本类型数据前:小猫
更改深拷贝非基本类型数据后:小猫
false
false