Java的深拷贝与浅拷贝
鄙人写文章喜欢简洁点,希望用尽可能短的语句描述一个知识点 1、概述 拷贝的一个经典的使用场景:当前对象要传给其他多个方法使用,如果该对象在某一个方法中被修改,那么这个修改会影响到其他方法。 如果要避免这种影响,就需要给每一个方法都传入一个当前对象的拷贝。 深与浅拷贝的区别就在于对复杂对象的处理:对于基本类型,浅拷贝、深拷贝都是拷贝的值;对于引用类型浅拷贝的是对象的引用。而深拷贝则是直接新建一个对象实例。 ? 注意浅拷贝中的复杂引用以及简单引用:对于简单引用,拷贝后的对象指向新的地址不会影响到原对象。复杂引用,拷贝后的对象将内部的对象指向新的地址,会影响到原对象对应的内部对象。这里一定理解下,抓住“地址”指向去理解就好了。 ? ? 2、代码实现 (1)浅拷贝实现方式:类实现Cloneable接口,并且重写clone()方法。详细见下面的代码 (2)深拷贝:主要是针对类内部的复杂引用变量。两种方式:1)复杂引用也实现Cloneable,并重写clone()方法,然后在父对象重写的clone方法中将该复杂引用指向克隆后的引用? 2)直接将需要克隆的对象进行序列化,然后反序列化就可以得到一个深拷贝的对象。 当然这些工作都有现成的轮子了,借助于Apache Commons工具类可以直接实现:
当然可以直接使用 2.1浅拷贝 父子类 1 @Data 2 @Builder 3 class Father implements Cloneable { 4 Long age; 5 StringBuilder name; 6 Son son; 7 8 @Override 9 public Object clone() { 10 //浅拷贝 11 try { 12 return super.clone(); 13 } catch (CloneNotSupportedException e) { 14 e.printStackTrace(); 15 return null; 16 } 17 } 18 } 19 20 @Data 21 @Builder 22 class Son { 23 Long age; 24 String name; 25 int grade; 26 } ? ? 初始赋值 1 public static Son son = Son.builder().age(new Long(30L)).name("大儿子").grade(100).build(); 2 public static Father father = Father.builder().age(new Long(50L)).name("爸爸").son(son).build(); ? 浅拷贝示例: private static void shallowClone() { System.out.println("父亲年龄:" + father.getAge()); System.out.println("父亲姓名:" + father.getName()); System.out.println("儿子年龄:" + father.getSon().getAge()); System.out.println("儿子姓名:" + father.getSon().getName()); System.out.println("儿子分数:" + father.getSon().getGrade()); //开始克隆 Father fatherInLaw = (Father) father.clone(); fatherInLaw.getSon().setAge(10L); fatherInLaw.getSon().setGrade(0); fatherInLaw.getSon().setName("继子"); fatherInLaw.setAge(new Long(80L)); fatherInLaw.setName("继父"); //修改后结果 System.out.println("==========浅拷贝后==========="); System.out.println("父亲年龄:" + father.getAge()); System.out.println("父亲姓名:" + father.getName()); System.out.println("儿子年龄:" + father.getSon().getAge()); System.out.println("儿子姓名:" + father.getSon().getName()); System.out.println("儿子分数:" + father.getSon().getGrade()); } 结果输出 父亲年龄:50 父亲姓名:爸爸 儿子年龄:30 儿子姓名:大儿子 儿子分数:100 ==========浅拷贝后=========== 父亲年龄:50 父亲姓名:爸爸 儿子年龄:10 儿子姓名:继子 儿子分数:0 ? 2.2.深拷贝 (1)子引用重写clone方法,并且在父类中改变子引用的指向 父子类 @Data @Builder class Father implements Cloneable { Long age; StringBuilder name; Son son; @Override public Object clone() { Father father = null; //浅拷贝 try { father = (Father) super.clone(); father.son = (Son) son.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } return father; } } @Data @Builder class Son implements Cloneable { Long age; String name; int grade; @Override public Object clone() { //拷贝 try { return super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); return null; } } } 赋初值 1 public static Son son = Son.builder().age(new Long(30L)).name("大儿子").grade(100).build(); 2 public static Father father = Father.builder().age(new Long(50)).name(new StringBuilder("爸爸")).son(son).build(); 深拷贝示例: //深拷贝 private static void deepClone() { System.out.println("父亲年龄:" + father.getAge()); System.out.println("父亲姓名:" + father.getName()); System.out.println("儿子年龄:" + father.getSon().getAge()); System.out.println("儿子姓名:" + father.getSon().getName()); System.out.println("儿子分数:" + father.getSon().getGrade()); //开始克隆 Father fatherInLaw = (Father) father.clone(); fatherInLaw.getSon().setAge(10L); fatherInLaw.getSon().setGrade(0); fatherInLaw.getSon().setName("继子"); fatherInLaw.setAge(new Long(80L)); fatherInLaw.setName(new StringBuilder("继父")); //修改前 System.out.println("==========浅拷贝后==========="); System.out.println("父亲年龄:" + father.getAge()); System.out.println("父亲姓名:" + father.getName()); System.out.println("儿子年龄:" + father.getSon().getAge()); System.out.println("儿子姓名:" + father.getSon().getName()); System.out.println("儿子分数:" + father.getSon().getGrade()); } 结果 父亲年龄:50 父亲姓名:爸爸 儿子年龄:30 儿子姓名:大儿子 儿子分数:100 ==========浅拷贝后=========== 父亲年龄:50 父亲姓名:爸爸 儿子年龄:30 儿子姓名:大儿子 儿子分数:100 (2)序列化方式 父子类 @Data @Builder class Father implements Serializable { Long age; StringBuilder name; Son son; public Object deepClone() throws IOException,ClassNotFoundException { //序列化 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); //反序列化 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return ois.readObject(); } } @Data @Builder class Son implements Serializable { Long age; String name; int grade; } 实现方法 //深拷贝 private static void deepCloneV2() throws IOException,ClassNotFoundException { System.out.println("父亲年龄:" + father.getAge()); System.out.println("父亲姓名:" + father.getName()); System.out.println("儿子年龄:" + father.getSon().getAge()); System.out.println("儿子姓名:" + father.getSon().getName()); System.out.println("儿子分数:" + father.getSon().getGrade()); //开始克隆 Father fatherInLaw = (Father) father.deepClone(); fatherInLaw.getSon().setAge(10L); fatherInLaw.getSon().setGrade(0); fatherInLaw.getSon().setName("继子"); fatherInLaw.setAge(new Long(80L)); fatherInLaw.setName(new StringBuilder("继父")); //修改前 System.out.println("==========浅拷贝后==========="); System.out.println("父亲年龄:" + father.getAge()); System.out.println("父亲姓名:" + father.getName()); System.out.println("儿子年龄:" + father.getSon().getAge()); System.out.println("儿子姓名:" + father.getSon().getName()); System.out.println("儿子分数:" + father.getSon().getGrade()); } 结果: 父亲年龄:50 父亲姓名:爸爸 儿子年龄:30 儿子姓名:大儿子 儿子分数:100 ==========浅拷贝后=========== 父亲年龄:50 父亲姓名:爸爸 儿子年龄:30 儿子姓名:大儿子 儿子分数:100 3、使用Apach??Commons 3.1实现深拷贝,需要实现序列化接口,SerializationUtils.clone(T object); 父子类 @Data @Builder class Father implements Serializable{ Long age; StringBuilder name; Son son; } @Data @Builder class Son implements Serializable{ Long age; String name; int grade; } 实现方法 //深拷贝 private static void deepCloneCommons() throws IOException,ClassNotFoundException { System.out.println("父亲年龄:" + father.getAge()); System.out.println("父亲姓名:" + father.getName()); System.out.println("儿子年龄:" + father.getSon().getAge()); System.out.println("儿子姓名:" + father.getSon().getName()); System.out.println("儿子分数:" + father.getSon().getGrade()); //开始克隆 Father fatherInLaw = (Father) SerializationUtils.clone(father); fatherInLaw.getSon().setAge(10L); fatherInLaw.getSon().setGrade(0); fatherInLaw.getSon().setName("继子"); fatherInLaw.setAge(new Long(80L)); fatherInLaw.setName(new StringBuilder("继父")); //修改前 System.out.println("==========浅拷贝后==========="); System.out.println("父亲年龄:" + father.getAge()); System.out.println("父亲姓名:" + father.getName()); System.out.println("儿子年龄:" + father.getSon().getAge()); System.out.println("儿子姓名:" + father.getSon().getName()); System.out.println("儿子分数:" + father.getSon().getGrade()); } 结果 父亲年龄:50 父亲姓名:爸爸 儿子年龄:30 儿子姓名:大儿子 儿子分数:100 ==========深拷贝后=========== 父亲年龄:50 父亲姓名:爸爸 儿子年龄:30 儿子姓名:大儿子 儿子分数:100 3.2使用工具类实现深拷贝,BeanUtils.copyProperties(Object dest,Object orig)直接调用即可实现浅拷贝,BeanUtils.cloneBean(Object obj)最终也是调用的copyProperties方法。 ? 今日学习笔记到此结束。 书于:2019/08/01/ 22:40分 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |