1. 程式人生 > >Object類clone方法徹底剖析

Object類clone方法徹底剖析

一、什麼是克隆

克隆就是依據已經有的資料,創造一份新的完全一樣的資料拷貝。

Java中物件的克隆有深克隆和淺克隆之分。有這種區分的原因是Java中分為基本資料型別和引用資料型別,對於不同的資料型別在記憶體中的儲存的區域是不同的。基本資料型別儲存在棧中,引用資料型別儲存在堆中。

二、為什麼要克隆

 克隆的物件可能包含一些已經修改過的屬性,保留著你想克隆物件的值,而new出來的物件的屬性全是一個新的物件,對應的屬性沒有值,所以我們還要重新給這個物件賦值。即當需要一個新的物件來儲存當前物件的“狀態”就靠clone方法了。那麼我把這個物件的臨時屬性一個一個的賦值給我新new的物件不也行嘛?可以是可以,但是一來麻煩不說,二來,大家通過上面的原始碼都發現了clone是一個native方法,就是快啊,在底層實現的。

三、如何克隆 

  1. 物件的類實現Cloneable介面;
  2. 覆蓋Object類的clone()方法 (覆蓋clone()方法,訪問修飾符設為public,預設是protected);
  3. 在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沒有絲毫關係!