1. 程式人生 > >Java必備技能:clone淺克隆與深克隆

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操作,這種方式的效率會比前面的低。

Java高架構師、分散式架構、高可擴充套件、高效能、高併發、效能優化、Spring boot、Redis、ActiveMQ、Nginx、Mycat、Netty、Jvm大型分散式專案實戰學習架構師視訊免費獲取架構群:854180697 群連結:https://jq.qq.com/?_wv=1027&k=5UwECNa

寫在最後:歡迎留言討論,加關注,持續更新!