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沒有絲毫關係! 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章