您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關怎么在Java中利用Clone實現深拷貝與淺拷貝,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。
package com.lxk.model; public class Student implements Cloneable { private String name; private Car car; public String getName() { return name; } public void setName(String name) { this.name = name; } public Car getCar() { return car; } public void setCar(Car car) { this.car = car; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + ", car=" + car + '}'; } @Override public Student clone() { Student student = null; try { student = (Student) super.clone(); } catch (CloneNotSupportedException ignored) { System.out.println(ignored.getMessage()); } return student; } }
學生內部引用了Car這個bean
package com.lxk.model; import java.util.List; public class Car implements Comparable<Car> { private String sign; private int price; private List<Dog> myDog; private List<String> boys; public Car() { } public Car(String sign, int price) { this.sign = sign; this.price = price; } public Car(String sign, int price, List<Dog> myDog) { this.sign = sign; this.price = price; this.myDog = myDog; } public Car(String sign, int price, List<Dog> myDog, List<String> boys) { this.sign = sign; this.price = price; this.myDog = myDog; this.boys = boys; } public String getSign() { return sign; } public void setSign(String sign) { this.sign = sign; } public int getPrice() { return price; } public void setPrice(int price) { this.price = price; } public List<Dog> getMyDog() { return myDog; } public void setMyDog(List<Dog> myDog) { this.myDog = myDog; } public List<String> getBoys() { return boys; } public void setBoys(List<String> boys) { this.boys = boys; } @Override public int compareTo(Car o) { //同理也可以根據sign屬性排序,就不舉例啦。 return this.getPrice() - o.getPrice(); } @Override public String toString() { return "Car{" + "sign='" + sign + '\'' + ", price=" + price + ", myDog=" + myDog + ", boys=" + boys + '}'; } }
最后就是main測試類
package com.lxk.findBugs; import com.lxk.model.Car; import com.lxk.model.Student; /** * 引用傳遞也就是地址傳遞需要注意的地方,引起的bug * <p> * Created by lxk on 2017/3/23 */ public class Bug2 { public static void main(String[] args) { Student student1 = new Student(); Car car = new Car("oooo", 100); student1.setCar(car); student1.setName("lxk"); //克隆完之后,student1和student2應該沒關系的,修改student1不影響student2的值,但是完之后發現,你修改car的值,student2也受影響啦。 Student student2 = student1.clone(); System.out.println("學生2:" + student2);//先輸出student2剛剛克隆完之后的值,然后在修改student1的相關引用類型的屬性值(car)和基本屬性值(name) car.setSign("X5"); student1.setName("xxx"); System.out.println("學生2:" + student2);//再次輸出看修改的結果 } }
之后就該是執行的結果圖了:
對上面執行結果的疑惑,以及解釋說明:
我們可能覺得自己在bean里面實現clone接口,重寫了這個clone方法,那么學生2是經由學生1clone,復制出來的,
那么學生1和學生2,應該是毫不相干的,各自是各自,然后,在修改學生1的時候,學生2是不會受影響的。
但是結果,不盡人意。從上圖執行結果可以看出來,除了名字,這個屬性是沒有被學生1影響,關于car的sign屬性已經因為學生1的變化而變化,這不是我希望的結果。
可見,這個簡單的克隆實現也僅僅是個“淺克隆”,也就是基本類型數據,他是會給你重新復制一份新的,但是引用類型的,他就不會重新復制份新的。引用類型包括,上面的其他bean的引用,list集合,等一些引用類型。
那么怎么實現深克隆呢?
對上述代碼稍作修改,如下:
學生bean的clone重寫方法如下所示:
@Override public Student clone() { Student student = null; try { student = (Student) super.clone(); if (car != null) { student.setCar(car.clone()); } } catch (CloneNotSupportedException ignored) { System.out.println(ignored.getMessage()); } return student; }
然后還要Car類實現cloneable接口,復寫clone方法:
@Override public Car clone() { Car car = null; try { car = (Car) super.clone(); if (myDog != null) { car.setMyDog(Lists.newArrayList(myDog)); } if (boys != null) { car.setBoys(Lists.newArrayList(boys)); } } catch (CloneNotSupportedException ignored) { System.out.println(ignored.getMessage()); } return car; }
主測試代碼不動,這個時候的執行結果如下:
可以看到,這個時候,你再修改學生1的值,就不會影響到學生2的值,這才是真正的克隆,也就是所謂的深克隆。
怎么舉一反三?
可以看到,這個例子里面的引用類型就一個Car類型的屬性,但是實際開發中,除了這個引用其他bean類型的屬性外,可能還要list類型的屬性值用的最多。
那么要怎么深克隆呢,就像我在Car bean類里面做的那樣,把所有的引用類型的屬性,都在clone一遍。那么你在最上層調用這個clone方法的時候,他就是真的深克隆啦。
我代碼里面那么判斷是為了避免空指針異常。當然,這個你也得注意咯。
注意 重寫clone方法的時候,里面各個屬性的null的判斷哦。
上面的是override clone()
方法來實現深克隆的。如果你這個要克隆的對象很復雜的話,你就不得不去每個引用到的對象去復寫這個clone方法,這個太啰嗦來,改的地方,太多啦。
還有個方法就是使用序列化來實現這個深拷貝
/** * 對象的深度克隆,此處的對象涉及Collection接口和Map接口下對象的深度克隆 * 利用序列化和反序列化的方式進行深度克隆對象 * * @param object 待克隆的對象 * @param <T> 待克隆對象的數據類型 * @return 已經深度克隆過的對象 */ public static <T extends Serializable> T deepCloneObject(T object) { T deepClone = null; ByteArrayOutputStream baos = null; ObjectOutputStream oos = null; ByteArrayInputStream bais = null; ObjectInputStream ois = null; try { baos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(baos); oos.writeObject(object); bais = new ByteArrayInputStream(baos .toByteArray()); ois = new ObjectInputStream(bais); deepClone = (T)ois.readObject(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } finally { try { if(baos != null) { baos.close(); } } catch (IOException e) { e.printStackTrace(); } try { if(oos != null) { oos.close(); } } catch (IOException e) { e.printStackTrace(); } try{ if(bais != null) { bais.close(); } } catch (IOException e) { e.printStackTrace(); } try{ if(ois != null) { ois.close(); } } catch (IOException e) { e.printStackTrace(); } } return deepClone; }
具體的使用如下:
/** * 使用序列化來實現深拷貝簡單。但是,所涉及到的所有對象都的實現序列化接口。 */ private static void cloneBySerializable() { Student student1 = new Student(); Car car = new Car("oooo", 100, Lists.newArrayList(new Dog("aaa", true, true))); student1.setCar(car); student1.setName("lxk"); Student student2 = deepCloneObject(student1); System.out.println("學生2:" + student2); car.setSign("X5"); car.setMyDog(null); student1.setName("xxx"); System.out.println("學生2:" + student2); }
Java是一門面向對象編程語言,可以編寫桌面應用程序、Web應用程序、分布式系統和嵌入式系統應用程序。
看完上述內容,你們對怎么在Java中利用Clone實現深拷貝與淺拷貝有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。