Java的Object的clone

在Android(以及更廣泛的Java編程環(huán)境)中,Object類的clone()方法用于創(chuàng)建并返回對(duì)象的一個(gè)副本。然而,該方法執(zhí)行的是淺拷貝(shallow copy)而非深拷貝(deep copy)。理解這兩者的區(qū)別對(duì)于正確使用clone()方法至關(guān)重要。

淺拷貝 vs 深拷貝

  • 淺拷貝(Shallow Copy)

    • 當(dāng)你對(duì)一個(gè)對(duì)象執(zhí)行淺拷貝時(shí),會(huì)創(chuàng)建一個(gè)新的對(duì)象,這個(gè)新對(duì)象有著與原對(duì)象相同的實(shí)例變量值。也就是說,基本數(shù)據(jù)類型的成員變量會(huì)被復(fù)制其值,而引用類型的成員變量則只是復(fù)制了引用地址,并沒有為這些引用類型創(chuàng)建新的實(shí)例。因此,原始對(duì)象和副本對(duì)象中的引用類型成員變量指向的是同一個(gè)內(nèi)存地址上的對(duì)象。
  • 深拷貝(Deep Copy)

    • 相比之下,深拷貝不僅會(huì)創(chuàng)建一個(gè)新的對(duì)象并復(fù)制所有基本數(shù)據(jù)類型的成員變量值,還會(huì)遞歸地復(fù)制所有引用類型的成員變量,使得原始對(duì)象和副本對(duì)象中的引用類型成員變量指向不同的對(duì)象。這意味著,即使兩個(gè)對(duì)象包含相同的內(nèi)容,它們之間也不會(huì)共享任何引用對(duì)象的狀態(tài)。

示例說明

假設(shè)我們有一個(gè)簡(jiǎn)單的類Person,它包含一個(gè)基本類型字段和一個(gè)引用類型字段:

class Person implements Cloneable {
    private String name;
    private Address address; // 假設(shè)這是一個(gè)自定義類

    public Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

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

    // getter and setter methods...
}

class Address {
    private String city;

    public Address(String city) {
        this.city = city;
    }

    // getter and setter methods...
}

如果我們對(duì)Person對(duì)象進(jìn)行淺拷貝:

Address addr = new Address("Beijing");
Person original = new Person("John", addr);
Person cloned = (Person) original.clone();

// 修改克隆對(duì)象的引用類型成員變量
cloned.getAddress().setCity("Shanghai");

System.out.println(original.getAddress().getCity()); // 輸出: Shanghai

你會(huì)發(fā)現(xiàn)修改克隆對(duì)象cloned中的address屬性也影響到了原始對(duì)象original,因?yàn)閮烧邔?shí)際上共享同一個(gè)Address對(duì)象實(shí)例。

為了實(shí)現(xiàn)深拷貝,你需要重寫clone()方法,并確保所有引用類型的成員變量也被適當(dāng)?shù)貜?fù)制:

@Override
protected Object clone() throws CloneNotSupportedException {
    Person cloned = (Person) super.clone();
    cloned.address = (Address) this.address.clone(); // 假設(shè)Address類也實(shí)現(xiàn)了Cloneable接口
    return cloned;
}

同時(shí),Address類也需要支持克?。?/p>

class Address implements Cloneable {
    private String city;

    public Address(String city) {
        this.city = city;
    }

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

    // getter and setter methods...
}

這樣,當(dāng)你再次嘗試修改克隆對(duì)象的address屬性時(shí),不會(huì)影響到原始對(duì)象,因?yàn)楝F(xiàn)在每個(gè)對(duì)象都有自己的獨(dú)立Address實(shí)例。

總結(jié)

  • 淺拷貝:只復(fù)制對(duì)象的基本數(shù)據(jù)類型成員變量的值;對(duì)于引用類型的成員變量,僅復(fù)制引用地址,不創(chuàng)建新的對(duì)象實(shí)例。
  • 深拷貝:不僅復(fù)制基本數(shù)據(jù)類型成員變量的值,還遞歸地復(fù)制所有引用類型的成員變量,確保每個(gè)對(duì)象都擁有獨(dú)立的引用對(duì)象實(shí)例。

理解這一點(diǎn)有助于避免因?qū)ο箝g意外共享狀態(tài)而導(dǎo)致的潛在問題。如果你的應(yīng)用場(chǎng)景需要完全獨(dú)立的對(duì)象副本,則可能需要實(shí)現(xiàn)深拷貝。

如果成員變量是int[]數(shù)組呢?會(huì)被深拷貝還是淺拷貝?


? 結(jié)論先說:

對(duì)于 int[] 這種基本類型數(shù)組,clone() 會(huì)創(chuàng)建一個(gè)新的數(shù)組對(duì)象,并把原數(shù)組的每個(gè)元素復(fù)制過去,所以內(nèi)容是獨(dú)立的。
從效果上看,對(duì) int[]clone() 行為類似于“深拷貝”,但嚴(yán)格來說,它仍然是淺拷貝機(jī)制的一部分,只是對(duì)數(shù)組類型有特殊處理。


?? 原理分析

Java 中 Object.clone() 對(duì)不同類型的成員變量處理方式如下:

成員變量類型 clone() 行為
基本數(shù)據(jù)類型(int, boolean, long 等) 直接復(fù)制值
基本類型數(shù)組(int[], byte[], boolean[] 等) 創(chuàng)建新數(shù)組,并復(fù)制每個(gè)元素(值復(fù)制)
引用類型(如 String, Object 復(fù)制引用(即兩個(gè)對(duì)象指向同一個(gè)對(duì)象)→ 典型淺拷貝
引用類型數(shù)組(如 String[], Person[] 創(chuàng)建新數(shù)組,但數(shù)組中的每個(gè)元素是引用復(fù)制(不是對(duì)象復(fù)制)→ 淺拷貝

? 示例:int[] 數(shù)組的 clone() 行為

class Data implements Cloneable {
    public int[] numbers;

    public Data(int[] numbers) {
        this.numbers = numbers;
    }

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

// 使用示例
public class Test {
    public static void main(String[] args) throws CloneNotSupportedException {
        int[] arr = {1, 2, 3, 4};
        Data data1 = new Data(arr);
        Data data2 = (Data) data1.clone();

        // 修改克隆對(duì)象的數(shù)組
        data2.numbers[0] = 999;

        System.out.println("data1.numbers[0] = " + data1.numbers[0]); // 輸出:1
        System.out.println("data2.numbers[0] = " + data2.numbers[0]); // 輸出:999
    }
}

?? 輸出結(jié)果:

data1.numbers[0] = 1
data2.numbers[0] = 999

? 說明:data1data2numbers 是兩個(gè)不同的數(shù)組對(duì)象,修改一個(gè)不會(huì)影響另一個(gè)。


? 為什么 int[] 的 clone() 是“值復(fù)制”?

因?yàn)?int[]基本類型數(shù)組,數(shù)組中的每個(gè)元素都是 int 值(不是對(duì)象引用),所以 clone() 時(shí)會(huì)創(chuàng)建一個(gè)新數(shù)組,并把原數(shù)組的每個(gè) int 值復(fù)制過去。

這相當(dāng)于:

int[] newArr = new int[oldArr.length];
for (int i = 0; i < oldArr.length; i++) {
    newArr[i] = oldArr[i]; // 復(fù)制的是值
}

所以它是“安全的”,不會(huì)出現(xiàn)共享數(shù)據(jù)的問題。


? 總結(jié)

情況 clone() 行為 是否獨(dú)立
int x 值復(fù)制 ? 獨(dú)立
int[] arr 創(chuàng)建新數(shù)組,復(fù)制每個(gè) int ? 獨(dú)立(效果上像深拷貝)
String s 引用復(fù)制 ?? 共享對(duì)象(但 String 不可變,通常安全)
Person obj 引用復(fù)制 ? 共享對(duì)象
Person[] arr 創(chuàng)建新數(shù)組,但每個(gè)元素是引用復(fù)制 ?? 數(shù)組獨(dú)立,但元素對(duì)象共享

? 最佳實(shí)踐建議

  • 如果類中包含數(shù)組(尤其是可變對(duì)象數(shù)組),不要依賴默認(rèn)的 clone()。
  • 如果需要真正的“深拷貝”,應(yīng)手動(dòng)重寫 clone(),并對(duì)引用類型和引用數(shù)組進(jìn)行深度復(fù)制。
  • 對(duì)于 int[],默認(rèn) clone() 通常是安全的,可以放心使用。

?? 一句話總結(jié):

int[] 數(shù)組在 clone() 時(shí)會(huì)被復(fù)制成一個(gè)新的數(shù)組對(duì)象,且每個(gè) int 元素的值也被復(fù)制,因此不會(huì)與原數(shù)組共享數(shù)據(jù),行為上等價(jià)于“深拷貝”,是安全的。但這仍是 Java 淺拷貝機(jī)制對(duì)基本類型數(shù)組的特殊處理。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • """1.個(gè)性化消息: 將用戶的姓名存到一個(gè)變量中,并向該用戶顯示一條消息。顯示的消息應(yīng)非常簡(jiǎn)單,如“Hello ...
    她即我命閱讀 5,329評(píng)論 0 6
  • 為了讓我有一個(gè)更快速、更精彩、更輝煌的成長,我將開始這段刻骨銘心的自我蛻變之旅!從今天開始,我將每天堅(jiān)持閱...
    李薇帆閱讀 2,248評(píng)論 1 4
  • 似乎最近一直都在路上,每次出來走的時(shí)候感受都會(huì)很不一樣。 1、感恩一直遇到好心人,很幸運(yùn)。在路上總是...
    時(shí)間里的花Lily閱讀 1,753評(píng)論 1 3
  • 1、expected an indented block 冒號(hào)后面是要寫上一定的內(nèi)容的(新手容易遺忘這一點(diǎn)); 縮...
    庵下桃花仙閱讀 1,103評(píng)論 1 2
  • 一、工具箱(多種工具共用一個(gè)快捷鍵的可同時(shí)按【Shift】加此快捷鍵選取)矩形、橢圓選框工具 【M】移動(dòng)工具 【V...
    墨雅丫閱讀 1,610評(píng)論 0 0

友情鏈接更多精彩內(nèi)容