Object類clone方法徹底剖析
一、什麼是克隆
克隆就是依據已經有的資料,創造一份新的完全一樣的資料拷貝。
在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沒有絲毫關係!