Java的深浅拷贝你了解吗?
1、概述
拷貝的一個經典的使用場景:當前對象要傳給其他多個方法使用,如果該對象在某一個方法中被修改,那么這個修改會影響到其他方法。 如果要避免這種影響,就需要給每一個方法都傳入一個當前對象的拷貝。
深與淺拷貝的區別就在于對復雜對象的處理:對于基本類型,淺拷貝、深拷貝都是拷貝的值;對于引用類型淺拷貝的是對象的引用。而深拷貝則是直接新建一個對象實例。
?
注意淺拷貝中的復雜引用以及簡單引用:對于簡單引用,拷貝后的對象指向新的地址不會影響到原對象。復雜引用,拷貝后的對象將內部的對象指向新的地址,會影響到原對象對應的內部對象。這里一定理解下,抓住“地址”指向去理解就好了。
?
?
2、代碼實現
(1)淺拷貝實現方式:類實現Cloneable接口,并且重寫clone()方法。詳細見下面的代碼
(2)深拷貝:主要是針對類內部的復雜引用變量。兩種方式:1)復雜引用也實現Cloneable,并重寫clone()方法,然后在父對象重寫的clone方法中將該復雜引用指向克隆后的引用? 2)直接將需要克隆的對象進行序列化,然后反序列化就可以得到一個深拷貝的對象。
當然這些工作都有現成的輪子了,借助于Apache Commons工具類可以直接實現:
- 淺克隆:BeanUtils.cloneBean(Object obj);
- 深克隆:SerializationUtils.clone(T object);
當然可以直接使用
2.1淺拷貝
父子類
1 @Data2 @Builder3 class Father implements Cloneable {4 Long age;5 StringBuilder name;6 Son son;7 8 @Override9 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;@Overridepublic 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;@Overridepublic Object clone() {//拷貝try {return super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();return null;}} }View Code
賦初值
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; }View Code
實現方法
//深拷貝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 兒子姓名:大兒子 兒子分數:1003、使用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; }View Code
實現方法
//深拷貝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());}View Code
結果
父親年齡:50 父親姓名:爸爸 兒子年齡:30 兒子姓名:大兒子 兒子分數:100 ==========深拷貝后=========== 父親年齡:50 父親姓名:爸爸 兒子年齡:30 兒子姓名:大兒子 兒子分數:1003.2使用工具類實現深拷貝,BeanUtils.copyProperties(Object dest, Object orig)直接調用即可實現淺拷貝,BeanUtils.cloneBean(Object obj)最終也是調用的copyProperties方法。
?
摘自:http://www.manongjc.com/detail/9-vvtzsyvyjxehgdw.html
總結
以上是生活随笔為你收集整理的Java的深浅拷贝你了解吗?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 我来更新了,说说工作中的Java处理异常
- 下一篇: 大剑无锋之Java的深浅拷贝解释一下!