Java深入學習26:Java深度克隆
深克隆和淺克隆區別
淺克隆: 只copy對象引用,不copy對象本身。即對象地址不變,仍然只存在一個對象。
深克隆: 不僅拷貝對象本身,而且拷貝對象包含的引用指向的所有對象。
深克隆的兩個方案
方案1——實現Cloneable接口,重寫Object類地 clone()方法
分如下三步
1. 對象的類實現Cloneable接口(必須,否則會拋出CloneNotSupportedException異常);
2. 覆蓋Object類的clone()方法 (覆蓋clone()方法,將訪問修飾符改為public,默認是protected);
3. 在clone()方法中調用super.clone();
public class CloneTest { public static void main(String[] args) throws CloneNotSupportedException { Student stu = new Student("A"); Student deep = stu.clone();//深克隆 Student shallow = stu;//淺克隆 shallow.setName("B"); System.out.println("original: " + stu);//Student{name='B'} System.out.println("deep: " + deep);//Student{name='A'} System.out.println("shallow: " + shallow);//Student{name='B'} } } class Student implements Cloneable{ private String name; public Student(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public Student clone() throws CloneNotSupportedException { return (Student) super.clone(); } @Override public String toString() { return "Student{" + "name='" + name + '\'' + '}'; } }
深克隆的多級深克隆問題:
重寫clone()方法,只會深克隆類和類中所有非基本數據類型的屬性對應的類。對於類中的屬性對應的類,是無法深克隆的,如果深克隆類中的屬性對應的類,需要額外的調用類中的屬性對應的類clone方法
public class CloneTest { public static void main(String[] args) throws CloneNotSupportedException { School sch = new School("a",new Student("A")); School deepSch = sch.clone();//深克隆 School shallowSch = sch;//淺克隆 shallowSch.getStu().setName("B"); System.out.println(sch);//School{name='a', stu=Student{name='B'}} System.out.println(deepSch);//School{name='a', stu=Student{name='A'}} System.out.println(shallowSch);// School{name='a', stu=Student{name='B'}} } } class School implements Cloneable{ private String name; private Student stu; @Override public School clone() throws CloneNotSupportedException { //return (School)super.clone();//該方法無法深克隆Sch中的Student School clone = (School) super.clone(); clone.setStu(this.stu.clone()); return clone; } public School(String name, Student stu) { this.name = name; this.stu = stu; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Student getStu() { return stu; } public void setStu(Student stu) { this.stu = stu; } @Override public String toString() { return "School{" + "name='" + name + '\'' + ", stu=" + stu + '}'; } }
方案2——序列化深克隆
什么是序列化?
1- java序列化是指把java對象轉換為字節序列的過程,而java反序列化是指把字節序列恢復為java對象的過程
2- 序列化:對象序列化的最主要的用處就是在傳遞和保存對象的時候,保證對象的完整性和可傳遞性。序列化是把對象轉換成有序字節流,以便在網絡上傳輸或者保存在本地文件中。序列化后的字節流保存的java對象的狀態以及相關的描述信息。序列化機制的核心作用就是對象狀態的保存與重建。
3- 反序列化:客戶端從文件中或網絡上獲得序列化后的對象字節流后,根據字節流中所保存的對象狀態及描述信息,通過反序列化重建對象。
4- 序列化就是把實體對象狀態按照一定的格式寫入到有序字節流,反序列化就是從有序字節流重建對象,恢復對象狀態
import java.io.*; public class SerializeDeepCloneTest { public static void main(String[] args) { SchoolSeri sch = new SchoolSeri("zjut",new StudentSeri("tyj")); SchoolSeri schSeri = null; System.out.println(sch); try { schSeri = SerializeDeepCloneUtil.deepClone(sch); System.out.println(sch); } catch (Exception e) { e.printStackTrace(); } System.out.println(sch == schSeri); } } class SerializeDeepCloneUtil{ //序列化深克隆 static <T extends Serializable> T deepClone(T obj){ if(obj ==null){ return null; } T cloneObj = null; //序列化 ByteArrayOutputStream bout = null; ObjectOutputStream oos = null; try { //創建字節數組輸出流 //new ByteArrayOutputStream() Creates a new byte array output stream. bout = new ByteArrayOutputStream(); //創建對象輸出流 //new ObjectOutputStream(OutputStream out): Creates an ObjectOutputStream that writes to the specified OutputStream. oos = new ObjectOutputStream(bout); //向對象輸出流中寫數據 //void writeObject(Object obj): Write the specified object to the ObjectOutputStream. oos.writeObject(obj); } catch (IOException e) { e.printStackTrace(); }finally { close(oos); close(bout); } //反序列化 ByteArrayInputStream bin = null; ObjectInputStream ois = null; try { //創建字節數組輸入流 //new ByteArrayInputStream(byte buf[]): Creates a ByteArrayInputStream so that it uses buf as its buffer array. bin = new ByteArrayInputStream(bout.toByteArray()); //創建對象輸入流 //new ObjectInputStream(InputStream in): Creates an ObjectInputStream that reads from the specified InputStream. ois = new ObjectInputStream(bin); //從對象輸入流中讀取數據 //Object readObject(): Read an object from the ObjectInputStream. cloneObj = (T)ois.readObject(); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } finally { close(ois); close(bin); } return cloneObj; } //關閉流 private static void close(Closeable closeable){ if(closeable != null){ try { closeable.close(); } catch (IOException e) { e.printStackTrace(); } } } }
public class SchoolSeri implements Serializable{ private String name; private StudentSeri stu; public SchoolSeri(String name, StudentSeri stu) { this.name = name; this.stu = stu; } public String getName() { return name; } public void setName(String name) { this.name = name; } public StudentSeri getStu() { return stu; } public void setStu(StudentSeri stu) { this.stu = stu; } @Override public String toString() { return "School{" + "name='" + name + '\'' + ", stu=" + stu + '}'; } } public class StudentSeri implements Serializable{ private String name; public StudentSeri(String name) { this.name = name; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Student{" + "name='" + name + '\'' + '}'; } }
END