一、什麼是克隆
克隆就是依據已經有的數據,創造一份新的完全一樣的數據拷貝。
在Java中對象的克隆有深克隆和淺克隆之分。有這種區分的原因是Java中分爲基本數據類型和引用數據類型,對於不同的數據類型在內存中的存儲的區域是不同的。基本數據類型存儲在棧中,引用數據類型存儲在堆中。
二、爲什麼要克隆
克隆的對象可能包含一些已經修改過的屬性,保留着你想克隆對象的值,而new出來的對象的屬性全是一個新的對象,對應的屬性沒有值,所以我們還要重新給這個對象賦值。即當需要一個新的對象來保存當前對象的“狀態”就靠clone方法了。那麼我把這個對象的臨時屬性一個一個的賦值給我新new的對象不也行嘛?可以是可以,但是一來麻煩不說,二來,大家通過上面的源碼都發現了clone是一個native方法,就是快啊,在底層實現的。
三、如何克隆
- 對象的類實現Cloneable接口;
- 覆蓋Object類的clone()方法 (覆蓋clone()方法,訪問修飾符設爲public,默認是protected);
- 在clone()方法中調用super.clone();
說明:
①爲什麼我們在派生類中覆蓋Object的clone()方法時,一定要調用super.clone()呢?在運行時刻,Object中的clone()識別出你要複製的是哪一個對象,然後爲此對象分配空間,並進行對象的複製,將原始對象的內容一一複製到新對象的存儲空間中。
②繼承自java.lang.Object類的clone()方法是淺複製,要想實現深克隆須重寫super.clone();
四、深克隆和淺克隆
淺克隆
指拷貝對象時僅僅拷貝對象本身(包括對象中的基本變量),而不拷貝對象包含的引用指向的對象。
public class Student implements Cloneable { private int age; private String name; ... @Override public String toString() { return "Student [age=" + age + ", name=" + name + "]"; } @Override public Object clone() throws CloneNotSupportedException { // TODO Auto-generated method stub return super.clone(); } /** * @param args * @throws CloneNotSupportedException */ public static void main(String[] args) throws CloneNotSupportedException { Student student1 = new Student(20, "張三"); Student student2 = (Student) student1.clone(); student2.setAge(22); System.out.println("student1:" + student1.getName() + "-->"+ student1.getAge()); System.out.println("student2:" + student2.getName() + "-->"+ student2.getAge()); } } 運行結果: student1:張三-->20 student2:張三-->22
注意:修改student2的age值 但是沒有影響 student1的值
如果對象中有其他對象的引用,淺克隆的話會出現什麼問題呢?
class Teacher implements Cloneable { private String name; private Student student; ... @Override public String toString() { return "Teacher [name=" + name + ", student=" + student + "]"; } @Override public Object clone() throws CloneNotSupportedException { // TODO Auto-generated method stub return super.clone(); } public static void main(String[] args) throws CloneNotSupportedException { Student s1 = new Student(); s1.setAge(20); s1.setName("張三"); Teacher teacher1 = new Teacher(); teacher1.setName("小趙老師"); teacher1.setStudent(s1); //爲什麼會出現以下結果, Teacher中的clone方法 Teacher teacher2 = (Teacher)teacher1.clone(); Student s2 = teacher2.getStudent(); s2.setName("李四"); s2.setAge(30); System.out.println("teacher1:"+teacher1); System.out.println("teacher2:"+teacher2); } } 運行結果: teacher1:Teacher [name=小趙老師, student=Student [age=30, name=李四]] teacher2:Teacher [name=小趙老師, student=Student [age=30, name=李四]
注意:teacher1的學生s1原本是張三,再對teache,1克隆後得到teacher2,由於默認super.clone()是淺克隆,所以當前的teacher2實際上就指向teacher1,所以得到teacher2的學生爲張三,修改爲李四後teacher1的學生也對應改變。
深克隆
不僅拷貝對象本身,而且拷貝對象包含的引用指向的所有對象。
class Teacher implements Cloneable { private String name; private Student student; ... @Override public Object clone() throws CloneNotSupportedException { // TODO Auto-generated method stub //注意以下代碼 Teacher teacher = (Teacher)super.clone(); teacher.setStudent((Student)teacher.getStudent().clone()); return teacher; } public static void main(String[] args) throws CloneNotSupportedException { Student s1 = new Student(); s1.setAge(20); s1.setName("張三"); Teacher teacher1 = new Teacher(); teacher1.setName("小趙老師"); teacher1.setStudent(s1); Teacher teacher2 = (Teacher)teacher1.clone(); teacher2.setName("小明老師"); Student s2 = teacher2.getStudent(); s2.setName("李四"); s2.setAge(30); System.out.println("teacher1:"+teacher1); System.out.println("teacher2:"+teacher2); } } 運行結果: teacher1:Teacher [name=小趙老師, student=Student [age=20, name=張三]] teacher2:Teacher [name=小明老師, student=Student [age=30, name=李四]]
深克隆後的老師teacher2爲新的對象,與之前的teacher1沒有絲毫關係!