如何实现对象克隆?

2019-09-22  

  • 实现 Cloneable 接口,重写 clone() 方法。
  • 不实现 Cloneable 接口,会报 CloneNotSupportedException 异常。

 
package constxiong.interview;
 
/**
 * 测试克隆
 * @author ConstXiong
 * @date 2019-06-18 11:21:21
 */
public class TestClone {
 
	public static void main(String[] args) throws CloneNotSupportedException {
		Person p1 = new Person(1, "ConstXiong");//创建对象 Person p1
		Person p2 = (Person)p1.clone();//克隆对象 p1
		p2.setName("其不答");//修改 p2的name属性,p1的name未变
		System.out.println(p1);
		System.out.println(p2);
	}
	
}
 
/**
 * 人
 * @author ConstXiong
 * @date 2019-06-18 11:54:35
 */
class Person implements Cloneable {
	
	private int pid;
	
	private String name;
	
	public Person(int pid, String name) {
		this.pid = pid;
		this.name = name;
		System.out.println("Person constructor call");
	}
 
	public int getPid() {
		return pid;
	}
 
	public void setPid(int pid) {
		this.pid = pid;
	}
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
 
	@Override
	protected Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
 
	@Override
	public String toString() {
		return "Person [pid:"+pid+", name:"+name+"]";
	}
	
}

 

打印结果

Person constructor call
Person [pid:1, name:ConstXiong]
Person [pid:1, name:其不答]

 

  • Object 的 clone() 方法是浅拷贝,即如果类中属性有自定义引用类型,只拷贝引用,不拷贝引用指向的对象。

可以使用下面的两种方法,完成 Person 对象的深拷贝。

方法1、对象的属性的Class 也实现 Cloneable 接口,在克隆对象时也手动克隆属性。

  
    @Override
	public Object clone() throws CloneNotSupportedException {
		DPerson p = (DPerson)super.clone();
		p.setFood((DFood)p.getFood().clone());
		return p;
	}
 

 

完整代码

package constxiong.interview;
 
/**
 * 测试克隆
 * @author ConstXiong
 * @date 2019-06-18 11:21:21
 */
public class TestManalDeepClone {
 
	public static void main(String[] args) throws Exception {
		DPerson p1 = new DPerson(1, "ConstXiong", new DFood("米饭"));//创建Person 对象 p1
		DPerson p2 = (DPerson)p1.clone();//克隆p1
		p2.setName("其不答");//修改p2的name属性
		p2.getFood().setName("面条");//修改p2的自定义引用类型 food 属性
		System.out.println(p1);//修改p2的自定义引用类型 food 属性被改变,p1的自定义引用类型 food 属性也随之改变,说明p2的food属性,只拷贝了引用,没有拷贝food对象
		System.out.println(p2);
	}
	
}
 
class DPerson implements Cloneable {
	
	private int pid;
	
	private String name;
	
	private DFood food;
	
	public DPerson(int pid, String name, DFood food) {
		this.pid = pid;
		this.name = name;
		this.food = food;
		System.out.println("Person constructor call");
	}
 
	public int getPid() {
		return pid;
	}
 
	public void setPid(int pid) {
		this.pid = pid;
	}
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
 
	@Override
	public Object clone() throws CloneNotSupportedException {
		DPerson p = (DPerson)super.clone();
		p.setFood((DFood)p.getFood().clone());
		return p;
	}
 
	@Override
	public String toString() {
		return "Person [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
	}
 
	public DFood getFood() {
		return food;
	}
 
	public void setFood(DFood food) {
		this.food = food;
	}
	
}
 
class DFood implements Cloneable{
	
	private String name;
	
	public DFood(String name) {
		this.name = name;
	}
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
 
	@Override
	public Object clone() throws CloneNotSupportedException {
		return super.clone();
	}
	
}

 

打印结果

Person constructor call
Person [pid:1, name:ConstXiong, food:米饭]
Person [pid:1, name:其不答, food:面条]

 

方法2、结合序列化(JDK java.io.Serializable 接口、JSON格式、XML格式等),完成深拷贝

结合 java.io.Serializable 接口,完成深拷贝

package constxiong.interview;
 
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 TestSeriazableClone {
 
	public static void main(String[] args) {
		SPerson p1 = new SPerson(1, "ConstXiong", new SFood("米饭"));//创建 SPerson 对象 p1
		SPerson p2 = (SPerson)p1.cloneBySerializable();//克隆 p1
		p2.setName("其不答");//修改 p2 的 name 属性
		p2.getFood().setName("面条");//修改 p2 的自定义引用类型 food 属性
		System.out.println(p1);//修改 p2 的自定义引用类型 food 属性被改变,p1的自定义引用类型 food 属性未随之改变,说明p2的food属性,只拷贝了引用和 food 对象
		System.out.println(p2);
	}
	
}
 
class SPerson implements Cloneable, Serializable {
	
	private static final long serialVersionUID = -7710144514831611031L;
 
	private int pid;
	
	private String name;
	
	private SFood food;
	
	public SPerson(int pid, String name, SFood food) {
		this.pid = pid;
		this.name = name;
		this.food = food;
		System.out.println("Person constructor call");
	}
 
	public int getPid() {
		return pid;
	}
 
	public void setPid(int pid) {
		this.pid = pid;
	}
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
 
	/**
	 * 通过序列化完成克隆
	 * @return
	 */
	public Object cloneBySerializable() {
		Object obj = null;
		try {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			ObjectOutputStream oos = new ObjectOutputStream(baos);
			oos.writeObject(this);
			ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
			ObjectInputStream ois = new ObjectInputStream(bais);
			obj = ois.readObject();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return obj;
	}
 
	@Override
	public String toString() {
		return "Person [pid:"+pid+", name:"+name+", food:"+food.getName()+"]";
	}
 
	public SFood getFood() {
		return food;
	}
 
	public void setFood(SFood food) {
		this.food = food;
	}
	
}
 
class SFood implements Serializable {
	
	private static final long serialVersionUID = -3443815804346831432L;
	
	private String name;
	
	public SFood(String name) {
		this.name = name;
	}
 
	public String getName() {
		return name;
	}
 
	public void setName(String name) {
		this.name = name;
	}
	
}

 

打印结果

Person constructor call
Person [pid:1, name:ConstXiong, food:米饭]
Person [pid:1, name:其不答, food:面条]

 

ConstXiong 备案号:苏ICP备16009629号-3