Java必備技能:clone淺克隆與深克隆
一直以來只知道Java有clone方法,該方法屬於Object的,對於什麼是淺克隆與深克隆就比較模糊了,現在就來補充學習一下。
概念
淺拷貝(淺克隆)複製出來的物件的所有變數都含有與原來的物件相同的值,而所有的對其他物件的引用仍然指向原來的物件。
深拷貝(深克隆)複製出來的所有變數都含有與原來的物件相同的值,那些引用其他物件的變數將指向複製出來的新物件,而不再是原有的那些被引用的物件。換言之,深複製把要複製的物件所引用的物件都複製了一遍。
實現
Student.java
public class Student implements Cloneable { private String name; private int age; private Teacher teacher; public Student(String name, int age, Teacher teacher) { this.name = name; this.age = age; this.teacher = teacher; } }
Teacher.java
public class Teacher implements Cloneable { private String name; private String course; public Teacher(String name, String course) { this.name = name; this.course = course; } }
Student類中包含有name,age和Teacher物件。
淺克隆
克隆物件實現Cloneable介面(該介面是一個標記介面),在克隆的方法裡面呼叫super.clone(),就會返回克隆後的物件。
public class Student implements Cloneable { private String name; private int age; private Teacher teacher; public Student(String name, int age, Teacher teacher) { this.name = name; this.age = age; this.teacher = teacher; } public Student clone() { Student student = null; try { student = (Student) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return student; } }
測試
public static void main(String args[]) throws IOException, ClassNotFoundException { Teacher teacher = new Teacher("王老師", "英語"); Student student = new Student("小明", 11, teacher); Student clone = student.clone(); clone.setName("小強"); clone.setAge(20); clone.getTeacher().setName("李老師"); System.out.println(student.getName() + " " + student.getAge()); System.out.println(clone.getName() + " " + clone.getAge()); System.out.println(clone.getTeacher() == student.getTeacher()); }
輸出:
小明 11
小強 20
true複製程式碼從上面結果可知,克隆出來的Student物件裡的name和age是新的,但是teacher是和原來的共享的,這就是淺克隆。
深克隆
Student.java
public class Student implements Cloneable { private String name; private int age; private Teacher teacher; public Student(String name, int age, Teacher teacher) { this.name = name; this.age = age; this.teacher = teacher; } public Student clone() { Student student = null; try { student = (Student) super.clone(); Teacher teacher = this.teacher.clone(); student.setTeacher(teacher); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return student; } }
Teacher.java
public class Teacher implements Cloneable { private String name; private String course; public Teacher(String name, String course) { this.name = name; this.course = course; } public Teacher clone() { Teacher clone = null; try { clone = (Teacher) super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return clone; } }
要實現深克隆的話,克隆物件裡面的物件型別也必須實現Cloneable介面並呼叫clone()。
測試:
public static void main(String args[]) throws IOException, ClassNotFoundException { Teacher teacher = new Teacher("王老師", "英語"); Student student = new Student("小明", 11, teacher); Student clone = student.clone(); clone.setName("小強"); clone.setAge(20); clone.getTeacher().setName("李老師"); System.out.println(student.getName() + " " + student.getAge()); System.out.println(clone.getName() + " " + clone.getAge()); System.out.println(clone.getTeacher() == student.getTeacher()); }
輸出:
小明 11
小強 20
false複製程式碼這時,兩個物件的中的Teacher就不是同一個物件了,實現了深克隆,但是如果要克隆的物件繼承鏈比較長的話要實現深克隆,就必須逐層地實現Cloneable,這個過程是比較麻煩的,不過還有一種方法可以簡便地實現深克隆。
serializable克隆
大家知道,Java可以把物件序列化寫進一個流裡面,反之也可以把物件從序列化流裡面讀取出來,但這一進一出,這個物件就不再是原來的物件了,就達到了克隆的要求。
public class Student implements Serializable { private String name; private int age; private Teacher teacher; public Student(String name, int age, Teacher teacher) { this.name = name; this.age = age; this.teacher = teacher; } public Student serializableClone() throws IOException, ClassNotFoundException { Student clone; ByteArrayOutputStream bo = new ByteArrayOutputStream(); ObjectOutputStream oo = new ObjectOutputStream(bo); oo.writeObject(this); ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray()); ObjectInputStream oi = new ObjectInputStream(bi); clone = (Student) oi.readObject(); return clone; } }
通過把物件寫進ByteArrayOutputStream裡,再把它讀取出來。注意這個過程中所有涉及的物件都必須實現Serializable介面,由於涉及IO操作,這種方式的效率會比前面的低。
寫在最後:歡迎留言討論,加關注,持續更新!