Item 11: Override clone judiciously

July 17, 2017 Update

關于克隆,深拷貝淺拷貝,今天又看了一下這個文章clone方法 --深拷貝與淺拷貝,寫得非常通俗易懂,我看了前半部分,終于是知道了shallow copy的意思。。后半部分希望以后有機會看看(不知猴年馬月了。。)。


先學習一下克隆。
Fantastic chance to remember a thing! 之前寫算法一直被ArrayList的傳遞confuse,常見的場景是一個ArrayList<ArrayList<Integer>> result;保存著很多cell: ArrayList<Integer> cell;但我每次result.add(cell);的時候,之前add進來的cell也跟著變了,所以要每次add之前要new一個ArrayList,比如result.add(new ArrayList<Integer>(cell));這樣才行。

為什么會這樣?因為:
Java中,在任何用"="向對象變量賦值的時候都是「引用傳遞」。
僅僅傳遞了「對象的引用」。就像傳遞了一個指針一樣,后續操作都是在原來的對象上操作的。另外一點,
Java中用對象的作為入口參數的傳遞則缺省為「引用傳遞」。

Object類的clone()最終調用的是native方法:

protected native Object clone() throws CloneNotSupportedException;

native方法的效率一般來說都是遠高于java中的非native方法。這也解釋了為 什么要用Object中clone()方法而不是先new一個類,然后把原始對象中的信息賦到新對象中,雖然這也實現了clone功能。

為什么必須implements Cloneable接口

如果覆寫clone,就必須implements Cloneable,否則會throw CloneNotSupportedException。算是一個約定。

//Object.java
    protected Object clone() throws CloneNotSupportedException {
        if (!(this instanceof Cloneable)) {
            throw new CloneNotSupportedException("Class " + getClass().getName() +
                                                 " doesn't implement Cloneable");
        }
        return internalClone();
    }

effective java說這是mixin接口。
Iteye的回答

  1. Cloneable是標示接口與java.io.Serializable類似,用于告知JVM該對象實現clone。并且super.clone()可以返回一個復制。
  1. 很多時候,會把protected的clone方法修改為public,暴露給其他人可以使用。

淺拷貝和深拷貝

淺拷貝:拷貝基本成員屬性,對于引用類型僅返回指向改地址的引用。


2.28
FROM API:
The general intent is that, for any object x, the expression:
根據convention,這兩點是必須是true的:

       x.clone() != x
       x.clone().getClass() == x.getClass()

這一點也要是true,但不是必須:

       x.clone().equals(x)

根據convention,返回的object應該通過super.clone獲取。如果一個類和它的superclasses(Object除外)遵循這個convention,那就有x.clone().getClass() == x.getClass().

通常,為了達到cloned objects是獨立的,需要修改super.clone返回的object中的1個或更多的fields,然后再返回(也就是深拷貝)。
如果一個class只包含primitive fields(基本類型)或者Immutable objects,super.clone返回的object中的fields就不需要再做修改。

FROM EFFECTIVE JAVA:
如果每個域包含一個基本類型的值,或者包含一個指向不可變對象的引用,那么被返回的對象則可能正式你需要的對象,在這種情況下就不需要再做處理。例如在第9條中的PhoneNumber類正是如此:

public final class PhoneNumber {
    private final short areaCode;
    private final short prefix;
    private final short lineNumber;

    public PhoneNumber(int areaCode, int prefix,
                        int lineNumber) {
        this.areaCode = (short) areaCode;
        this.prefix = (short) prefix;
        this.lineNumber = (short) lineNumber;
    }
}

那就只需要:

@Override
public PhoneNumber clone() {
    try {
        return (PhoneNumber) super.clone();
    } catch(CloneNotSupportedException e) {
        throw new AssertionError();
    }
}

這里clone方法返回的是PhoneNumber,而不是返回的Object。從Java1.5發行版本開始,這么做就是合法的,因為1.5發行版本引入了協變返回類型(convariant return type)作為泛型。體現了一條原則:永遠不要讓客戶去做任何類庫能夠替客戶完成的事情。

對于下面這樣的類就不能直接return super.clone了,size域中具有正確的值,但是它的elements域將引用與原Stack實例相同的數組。

public class Stack {
    private Object[] elements;
    private int size = 0;
    private static final int DEFAULT_INITIAL_CAPACITY = 16;

    publci Stack() {
        elements = new Object[DEFAULT_INITIAL_CAPACITY];
    }

    public void push(Object e) {
        ensureCapacity();
        elements[size++] = e;
    }

    public Object pop() {
        if (0 == size) throw new EmptyStackException();
        return elements[--size];
    }

    /**
     * Ensure space for at least one more element
     * roughly doubling the capacity each time the 
     * array needs to grow
     * /
    private void ensureCapacity() {
        if (size == elements.length)
            elements = Arrays.copyOf(elements, 2* size + 1);
    }
}

這時候需要深拷貝了,可以遞歸:

@Override
public Stack clone() {
    try {
        Stack result = (Stack) super.clone();
        result.elements = elements.clone();
        return result;
    } catch (CloneNotSupportedException e) {
        throw new AssertionError();
    }
}

或者bucket clone:

@Override
public HashTable clone() {
    try {
        HashTable result = (HashTable) super.clone();
        result.buckets = buckets.clone();
        return result;
    } catch (CloneNotSupportedException e) {
        throw new AssertionError();
    }
}

這里不細談了,有點復雜,具體看書吧。

。。。
總之,deep copy很麻煩的樣子。書上說,最好不要覆蓋,也少去調用(因為完全可以new一個..),這里我就不深挖了。


See also:
http://blog.csdn.net/Jing_Unique_Da/article/details/49901933
http://lovelace.iteye.com/blog/182772
http://www.cnblogs.com/tonyluis/p/5778266.html
http://www.oschina.net/translate/java-copy-shallow-vs-deep-in-which-you-will-swim
https://zhidao.baidu.com/question/546603399.html

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,763評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,238評論 3 428
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,823評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,604評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,339評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,713評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,712評論 3 445
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,893評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,448評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,201評論 3 357
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,397評論 1 372
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,944評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,631評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,033評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,321評論 1 293
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,128評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,347評論 2 377

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,733評論 18 399
  • 對象的創建與銷毀 Item 1: 使用static工廠方法,而不是構造函數創建對象:僅僅是創建對象的方法,并非Fa...
    孫小磊閱讀 2,017評論 0 3
  • 11.Override clone judiciously 大意為 明智地重寫clone方法 說到clone方法,...
    Mezereon閱讀 813評論 0 3
  • 前言 人生苦多,快來 Kotlin ,快速學習Kotlin! 什么是Kotlin? Kotlin 是種靜態類型編程...
    任半生囂狂閱讀 26,249評論 9 118
  • (接上)到了酒店,問好。真正的客人還沒來,突然發現,他左臂上戴著孝章,忙問,老爺子他……?他說一個月前走的,默哀。...
    唐流泉閱讀 172評論 0 0