介紹
一直以來只知道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克隆的一點理解
如有不對的地方,歡迎指正。