天天看點

Java 深度解析clone方法

 在實際程式設計過程中,我們常常要遇到這種情況:有一個對象 A,在某一時刻 A 中已經包含了一些有效值,此時可能會需要一個和 A 完全相同新對象 B,并且此後對 B 任何改動都不會影響到 A 中的值,也就是說, A 與 B 是兩個獨立的對象,但 B 的初始值是由 A 對象确定的。在 Java 語言中,用簡單的指派語句是不能滿足這種需求的。因為java中的指派語句采用的是值傳遞,即多個棧空間(屬性名)引用同一片堆空間記憶體,對其中一個屬性修改,勢必影響到所有引用該堆空間對象的屬性。

 要滿足這種需求雖然有很多途徑,但采用clone()方法是其中最簡單,也是最高效的手段.。

protected native Object clone() throws CloneNotSupportedException;
           

 clone方法是Object類中的一個方法,可以通過clone進行對象的拷貝操作。

1. Cloneable接口

 如果想要使用clone方法,單純的覆寫Object的clone方法是不能使用的,他會抛出一個CloneNotSupportedException異常。

 要想正确使用,該對象的類要實作一個Cloneable辨別接口。

public interface Cloneable {
}
           

 Cloneable接口中沒有任何方法,它存在的意義就是允許其實作類使用clone方法。是以正确的使用流程應該是這樣的:

class Person implements Cloneable {

    @Override
    protected Object clone() throws CloneNotSupportedException {
       return super.clone();
    }

}
           

 Cloneable接口位于java.lang 包中,java.lang包已經被預設的導入類中,是以不需要寫成java.lang.Cloneable。

2. clone和new的差別

 new 操作符的本意是在堆上開辟記憶體。程式執行到 new 操作符時,首先去看 new 操作符後面的類型,因為知道了類型,才能知道要配置設定多大的記憶體空間。配置設定完記憶體之後,再調用構造函數,填充對象的各個域,這一步叫做對象的初始化,構造方法傳回後,一個對象建立完畢,可以把他的引用(位址)釋出到外部,在外部就可以使用這個引用操縱(屬性名)這個對象。

 clone 在第一步是和 new 相似的,都是配置設定記憶體,調用 clone 方法時,配置設定的記憶體和原對象(即調用 clone 方法的對象)相同,然後再使用原對象中對應的各個域,填充新對象的域,填充完成之後, clone 方法傳回,一個新的相同的對象被建立,這時我們就可以在外部對其進行通路。

 在前面clone方法的定義中我們可以看到,clone是一個native本地方法,native方法的效率一般來說都是遠高于java中的非native方法。是以一般我們使用clone方法,直接調用super.clone()即可。

3. clone方法的使用

class Person implements Cloneable {
    public int age;
    public String name;

    public Person(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", name='" + name + '\'' +
                '}';
    }
}


public class Demo {

    public static void main(String[] args) {
        Person person1 = new Person(, "xucc");
        Person person2 = null;
        try {
            person2 = (Person) person1.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        person1.age = ;
        person1.name = "licc";
        System.out.println(person1);
        System.out.println(person2);
    }
}
           

 運作結果:

com.xucc.exercise.Person@282ba1e
Person{age=, name='licc'}
com.xucc.exercise.Person@30c7da1e
Person{age=, name='xucc'}
           

 我們可以看出,clone确實開辟了新的空間,并且得到了與原有對象相同的值。

 最後,我們應該注意的是,clone()雖然建立了新的對象開辟了新的記憶體空間,但是對象中包含的一些屬性,并沒有進行重新建立,例如字元串,還是會從共享池中複用,是以clone屬于淺拷貝。

 關于進一步的分析會在之後的文章中展現。