标签:
今天来学习学习java对象的克隆,在写代码的时候,有时候我们会这样写:对象1=对象2,也就是把对象2赋值给对象1了,但是这样做有个问题,就是如果我们修改了对象2的属性值,对象1的相同属性值也被修改了,反过来亦如此,让我来证明一下:
public class A { private String msg; public A() { msg = "Hello"; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } }
public class Main { public static void main(String[] args) { A a1 = new A(); A a2 = new A(); System.out.println("a1.msg: " + a1.getMsg()); a1 = a2; a2.setMsg("World"); System.out.println("a1.msg: " + a1.getMsg()); } }
运行结果:
从Main类可以看到,一开始我们new了两个对象,也就是在JVM堆内存开辟了两块内存空间,接着把a2赋值给a1,这就意味着a2和a1对象指向了栈内存中同一个引用,这个时候我们修改a2对象的属性,a1对象的属性也同时会改变,反过来亦如此,例如我现在修改a1的属性来试试看a2的属性是否被改变了:
public class Main { public static void main(String[] args) { A a1 = new A(); A a2 = new A(); System.out.println("a1.msg: " + a1.getMsg()); a1 = a2; a1.setMsg("Java"); System.out.println("a2.msg: " + a2.getMsg()); } }
毫无悬念地改变了,也就是说使用“=”操作符会让两个对象指向同一个引用,两个对象的属性值是互通的,一个对象的改变必然会同步到另一个对象上面,但是有时候我们并不想一个对象的改变引起另一个对象的改变,我们希望a2对象被创建后含有a1对象初始的值,但是a2对象属性的改变不会影响a1对象,这样我们就应该使用对象的克隆,对象的克隆又分浅克隆和深克隆,我们先来看看浅克隆的实现方式:
public class A implements Cloneable { private String msg; public A() { msg = "Hello"; } public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
public class Main { public static void main(String[] args) throws CloneNotSupportedException { A a1 = new A(); System.out.println("a1.msg: " + a1.getMsg()); A a2 = (A) a1.clone(); System.out.println("a2.msg: " + a2.getMsg()); a2.setMsg("Java"); System.out.println("a1.msg: " + a1.getMsg()); System.out.println("a2.msg: " + a2.getMsg()); } }
可以看到实现对象的克隆比较简单,首先该类应该实现Cloneable接口并覆盖Object根类的clone方法,注意Cloneable只是一个标记接口,里面并没有定义任何方法,clone方法里面的实现也很简单,调用父类Object的clone方法就好了。现在看看Main类就知道,a2对象的msg初始化是"Hello",把a2的msg属性的值改为"Java"后,并没有影响a1对象的msg值,其值仍然是"Hello". 我们刚才看到的是浅克隆,没有集合属性,如果有集合属性的话,浅克隆就应付不了这种情况了,我们来看看代码:
public class A implements Cloneable { private List<String> list; public A() { list = new ArrayList<String>(); list.add("Hello"); list.add("World"); } public List<String> getList() { return list; } public void setList(List<String> list) { this.list = list; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } }
public class Main { public static void main(String[] args) throws CloneNotSupportedException { A a1 = new A(); System.out.println("a1.list: " + a1.getList()); A a2 = (A) a1.clone(); System.out.println("a2.list: " + a2.getList()); a2.getList().add("Java"); System.out.println("a1.list: " + a1.getList()); System.out.println("a2.list: " + a2.getList()); } }
大家看看,神奇的魔咒又出现了,即使我们实现了对象的克隆,但是因为对象的属性是集合类,这样我们修改a2对象的集合类中的值,也一定会影响a1对象集合类的值, 这是因为a1和a2对象的list属性是指向同一个内存地址的,面对这种情况,我们需要使用到对象的深克隆,具体怎么实现呢,来看代码:
public class A implements Cloneable { private List<String> list; public A() { list = new ArrayList<String>(); list.add("Hello"); list.add("World"); } public List<String> getList() { return list; } public void setList(List<String> list) { this.list = list; } @Override protected A clone() throws CloneNotSupportedException { A other = (A) super.clone(); List<String> otherList = new ArrayList<String>(); for (String s : list) { otherList.add(s); } other.setList(otherList); return other; } }
public class Main { public static void main(String[] args) throws CloneNotSupportedException { A a1 = new A(); System.out.println("a1.list: " + a1.getList()); A a2 = (A) a1.clone(); System.out.println("a2.list: " + a2.getList()); a2.getList().add("Java"); System.out.println("a1.list: " + a1.getList()); System.out.println("a2.list: " + a2.getList()); } }
大家可以看到,深克隆的实现也就是要在clone方法里面new一个list,并把原来对象list中的值赋值到新list中并把新list设置到新对象,说起来好拗口啊,这样a2.list值得改变不会影响a1.list了,想想如果原始类的属性是一个对象,而对象的属性又有对象和集合类,那么clone方法里面的代码就会很大,因为深克隆必须保证全面的克隆。
标签:
原文地址:http://www.cnblogs.com/stonefeng/p/5808178.html