java拷贝--clone
大纲:
- java如何拷贝对象。
- 浅拷贝
- 深拷贝
一、java如何拷贝对象
Person p = new Person(); Person p2 = p;
上例并不是一个拷贝操作,只是把p对象的引用赋给了p2,2个变量指向了同一片heap地址。
想实现拷贝操作需要做2件事情:
(1)实现Cloneable接口,这是一个空接口不用重写任何方法。
public interface Cloneable {}
(2)重写clone方法,注意clone方法是Object类中的native方法,并不是Cloneable接口的。
二、浅拷贝
改写Person类
@Data public class Person implements Cloneable { private String name; private int age; private Date birth; @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", birth=" + birth.getTime() + '}'; } @Override public Person clone() { Object clone = null; try { clone = super.clone(); } catch (CloneNotSupportedException e) {} return (Person)clone; } }
调用clone方法复制对象:
Person p1 = new Person(); Person p2 = p1.clone();
这样p1,p2就不是一个对象了。
三、深拷贝
以上Person对象中的成员变量都是java原生对象和基础数据类型,浅拷贝足以使Person拥有拷贝的功能。
如果为Person加上一个自定类的成员变量
@Data public class Father { private String name; private int age; @Override public String toString() { return "Father{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
public class Person implements Cloneable { private String name; private int age; private Date birth; private Father father; }
这样做后person中的father对象无法实现拷贝
测试例:
public static void main(String[] args){ Person p1 = new Person(); p1.setName("p1"); Father f1 = new Father(); f1.setName("f1"); p1.setFather(f1); Person p2 = (Person) p1.clone(); p2.getFather().setName("f2"); p2.setName("p2"); System.out.println(p1); System.out.println(p2); //结果 //Person{name='p1', age=0, birth=null, father=Father{name='f2', age=0}} //Person{name='p2', age=0, birth=null, father=Father{name='f2', age=0}} }
可以看到person对象实现了拷贝,但是father对象还是引用了同一个对象。
深拷贝实现步骤:
(1)使自定义的类的成员变量实现Cloneable接口并重写clone方法。
@Data public class Father implements Cloneable{ private String name; private int age; @Override public String toString() { return "Father{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public Father clone() { Object clone = null; try { clone = super.clone(); } catch (CloneNotSupportedException e) {} return (Father) clone; } }
(2)成员变量重写clone方法后,还需要将成员变量的拷贝对象显式赋值。
@Data public class Person implements Cloneable { private String name; private int age; private Date birth; private Father father; @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", birth=" + birth + ", father=" + father + '}'; } @Override public Person clone() { Person clone = null; try { clone = (Person) super.clone(); } catch (CloneNotSupportedException e) {} //这里显式赋值 if(father!=null){ clone.setFather(father.clone()); } return clone; } }
至此person中的father变量也完成了拷贝,可用上面的测试例子验证一下。
public static void main(String[] args){ Person p1 = new Person(); p1.setName("p1"); Father f1 = new Father(); f1.setName("f1"); p1.setFather(f1); Person p2 = (Person) p1.clone(); p2.getFather().setName("f2"); p2.setName("p2"); System.out.println(p1); System.out.println(p2); //结果 //Person{name='p1', age=0, birth=null, father=Father{name='f1', age=0}} //Person{name='p2', age=0, birth=null, father=Father{name='f2', age=0}} }