深克隆與淺克隆

一灭衷、克隆的作用

快速構(gòu)建一個和已有對象相同的副本,創(chuàng)建一個新對象旁涤,將已有對象的數(shù)據(jù)導入到新對象里面翔曲;

二、克隆基本簡介

我們說的克隆拭抬,都是基于超類 Object 來的部默,里面有個native方法,具體實現(xiàn)是它調(diào)用底層C語言的實現(xiàn)造虎,我們是看不到的

protected native Object clone() throws CloneNotSupportedException;

由此可知傅蹂,有幾個約束

  1. 使用時必須繼承Object類,我們所有的類都是Object派生的
  2. 接收對象必須強轉(zhuǎn)
  3. 必須實現(xiàn) Cloneable 接口標識 (表示重寫了clone() )

不實現(xiàn)這個接口會發(fā)生什么呢算凿?

public class User{

    private String userName;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

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

    public static void main(String[] args) throws CloneNotSupportedException {
        User user=new User();
        user.setUserName("zhangsan");
        System.out.println(JSONObject.toJSONString(user));
        User user1= (User) user.clone();
        System.out.println(JSONObject.toJSONString(user1));
    }
}

結(jié)果:

{"userName":"zhangsan"}
Exception in thread "main" java.lang.CloneNotSupportedException: com.demo.cs.study.clones.User
    at java.lang.Object.clone(Native Method)
    at com.demo.cs.study.clones.User.clone(User.java:19)
    at com.demo.cs.study.clones.User.main(User.java:32)

添加實現(xiàn)后

public class User implements Cloneable{

    private String userName;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

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

    public static void main(String[] args) throws CloneNotSupportedException {
        User user=new User();
        user.setUserName("zhangsan");
        System.out.println(JSONObject.toJSONString(user));
        User user1= (User) user.clone();
        System.out.println(JSONObject.toJSONString(user1));
    }
}

結(jié)果:

{"userName":"zhangsan"}
{"userName":"zhangsan"}

1份蝴、淺克隆

概念:如果被復制的對象所有的變量與原來的變量值相同,且所持有對其它對象的引用任然指向原來的對象就叫淺克隆

User.java

public class User implements Cloneable{

    private String userName;

    private HavingDinner havingDinner;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public HavingDinner getHavingDinner() {
        return havingDinner;
    }

    public void setHavingDinner(HavingDinner havingDinner) {
        this.havingDinner = havingDinner;
    }

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

HavingDinner.java

public class HavingDinner {

    private String foodName;

    public String getFoodName() {
        return foodName;
    }

    public void setFoodName(String foodName) {
        this.foodName = foodName;
    }

    public HavingDinner(String foodName) {
        this.foodName = foodName;
    }
}

Demo.java

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        HavingDinner hd=new HavingDinner("青椒炒蘿卜");
        User user=new User();
        user.setUserName("哈比");
        user.setHavingDinner(hd);
        User user1= (User) user.clone();
        user1.setUserName("哈比2");
        User user2= (User) user.clone();
        user2.setUserName("哈士奇");
        System.out.println(user.getUserName()+"-->今天晚上要吃"+user.getHavingDinner().getFoodName()+"  引用地址:"+user.getHavingDinner());
        System.out.println(user1.getUserName()+"-->今天晚上要吃"+user1.getHavingDinner().getFoodName()+"  引用地址:"+user1.getHavingDinner());
        System.out.println(user2.getUserName()+"-->今天晚上要吃"+user2.getHavingDinner().getFoodName()+"  引用地址:"+user2.getHavingDinner());
    }
}

結(jié)果:

哈比-->今天晚上要吃青椒炒蘿卜  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈比2-->今天晚上要吃青椒炒蘿卜  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈士奇-->今天晚上要吃青椒炒蘿卜  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380

看輸出結(jié)果氓轰,引用地址一樣婚夫。這就是淺克隆署鸡;那么問題來了案糙,哈比要吃土豆雞塊,我們改下

Demo2

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        HavingDinner hd=new HavingDinner("土豆雞塊");
        User user=new User();
        user.setUserName("哈比");
        user.setHavingDinner(hd);
        User user1= (User) user.clone();
        user1.setUserName("哈比2");
        User user2= (User) user.clone();
        user2.setUserName("哈士奇");
        System.out.println(user.getUserName()+"-->今天晚上要吃"+user.getHavingDinner().getFoodName()+"  引用地址:"+user.getHavingDinner());
        System.out.println(user1.getUserName()+"-->今天晚上要吃"+user1.getHavingDinner().getFoodName()+"  引用地址:"+user1.getHavingDinner());
        System.out.println(user2.getUserName()+"-->今天晚上要吃"+user2.getHavingDinner().getFoodName()+"  引用地址:"+user2.getHavingDinner());
    }
}

結(jié)果:

哈比-->今天晚上要吃土豆雞塊  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈比2-->今天晚上要吃土豆雞塊  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈士奇-->今天晚上要吃土豆雞塊  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380

會發(fā)現(xiàn)其它人都給吃土豆雞塊了靴庆,我們再改下哈比2的試試

Demo3:

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        HavingDinner hd=new HavingDinner("土豆雞塊");
        User user=new User();
        user.setUserName("哈比");
        user.setHavingDinner(hd);
        User user1= (User) user.clone();
        user1.setUserName("哈比2");
        user1.getHavingDinner().setFoodName("冰淇淋");
        User user2= (User) user.clone();
        user2.setUserName("哈士奇");
        System.out.println(user.getUserName()+"-->今天晚上要吃"+user.getHavingDinner().getFoodName()+"  引用地址:"+user.getHavingDinner());
        System.out.println(user1.getUserName()+"-->今天晚上要吃"+user1.getHavingDinner().getFoodName()+"  引用地址:"+user1.getHavingDinner());
        System.out.println(user2.getUserName()+"-->今天晚上要吃"+user2.getHavingDinner().getFoodName()+"  引用地址:"+user2.getHavingDinner());
    }
}

結(jié)果:

哈比-->今天晚上要吃冰淇淋  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈比2-->今天晚上要吃冰淇淋  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈士奇-->今天晚上要吃冰淇淋  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380

結(jié)果還是 都吃冰淇淋时捌,這樣的話就有問題了,我想哈比吃土豆雞塊炉抒,哈比2吃冰淇淋奢讨;
這樣也證實了上面的概念;基本屬性的值是復制了焰薄,但是其它對象里的屬性根本就沒有復制拿诸,只是單純的復制了引用地址扒袖;

2、深克隆

User.java

public class User implements Cloneable{

    private String userName;

    private HavingDinner havingDinner;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public HavingDinner getHavingDinner() {
        return havingDinner;
    }

    public void setHavingDinner(HavingDinner havingDinner) {
        this.havingDinner = havingDinner;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException{
        User user=(User) super.clone();
        user.setHavingDinner((HavingDinner) getHavingDinner().clone());
        return user;
    }
}

HavingDinner.java

public class HavingDinner implements Cloneable{

    private String foodName;

    public String getFoodName() {
        return foodName;
    }

    public void setFoodName(String foodName) {
        this.foodName = foodName;
    }

    public HavingDinner(String foodName) {
        this.foodName = foodName;
    }

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

Demo.java

public class Demo {
    public static void main(String[] args) throws CloneNotSupportedException {
        HavingDinner hd=new HavingDinner("土豆雞塊");
        User user=new User();
        user.setUserName("哈比");
        user.setHavingDinner(hd);
        User user1= (User) user.clone();
        user1.setUserName("哈比2");
        user1.getHavingDinner().setFoodName("冰淇淋");
        User user2= (User) user.clone();
        user2.setUserName("哈士奇");
        System.out.println(user.getUserName()+"-->今天晚上要吃"+user.getHavingDinner().getFoodName()+"  引用地址:"+user.getHavingDinner());
        System.out.println(user1.getUserName()+"-->今天晚上要吃"+user1.getHavingDinner().getFoodName()+"  引用地址:"+user1.getHavingDinner());
        System.out.println(user2.getUserName()+"-->今天晚上要吃"+user2.getHavingDinner().getFoodName()+"  引用地址:"+user2.getHavingDinner());
    }
}

結(jié)果:

哈比-->今天晚上要吃土豆雞塊  引用地址:com.demo.cs.study.clones.HavingDinner@12a3a380
哈比2-->今天晚上要吃冰淇淋  引用地址:com.demo.cs.study.clones.HavingDinner@29453f44
哈士奇-->今天晚上要吃土豆雞塊  引用地址:com.demo.cs.study.clones.HavingDinner@5cad8086

總結(jié):達到了互不影響的結(jié)果亩码,對象里面包含的對象也復制了季率,這是第一種方式,有點復雜蟀伸;
2蚀同、序列化深克隆
原理:把對象序列化輸出到流里面,然后把流里面的數(shù)據(jù)序列化出來啊掏,得到一個新的對象
事例:
User.java

public class User implements Serializable {

    private String userName;

    private HavingDinner havingDinner;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public HavingDinner getHavingDinner() {
        return havingDinner;
    }

    public void setHavingDinner(HavingDinner havingDinner) {
        this.havingDinner = havingDinner;
    }

    protected User deepClone() throws IOException, ClassNotFoundException {
        ByteArrayOutputStream byteArrayOutputStream=new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream=new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(this);
        ByteArrayInputStream byteArrayInputStream=new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        ObjectInputStream objectInputStream=new ObjectInputStream(byteArrayInputStream);
        return (User) objectInputStream.readObject();
    }
}

HavingDinner.java

public class HavingDinner implements Serializable {

    private String foodName;

    public String getFoodName() {
        return foodName;
    }

    public void setFoodName(String foodName) {
        this.foodName = foodName;
    }

    public HavingDinner(String foodName) {
        this.foodName = foodName;
    }
}

測試類

public class Demo {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        HavingDinner hd=new HavingDinner("土豆雞塊");
        User user=new User();
        user.setUserName("哈比");
        user.setHavingDinner(hd);
        User user1=user.deepClone();
        user1.setUserName("哈比2");
        user1.getHavingDinner().setFoodName("冰淇淋");
        User user2=user.deepClone();
        user2.setUserName("哈士奇");
        System.out.println(user.getUserName()+"-->今天晚上要吃"+user.getHavingDinner().getFoodName()+"  引用地址:"+user.getHavingDinner());
        System.out.println(user1.getUserName()+"-->今天晚上要吃"+user1.getHavingDinner().getFoodName()+"  引用地址:"+user1.getHavingDinner());
        System.out.println(user2.getUserName()+"-->今天晚上要吃"+user2.getHavingDinner().getFoodName()+"  引用地址:"+user2.getHavingDinner());
    }
}

結(jié)果:

哈比-->今天晚上要吃土豆雞塊  引用地址:com.demo.cs.study.clones.HavingDinner@5e2de80c
哈比2-->今天晚上要吃冰淇淋  引用地址:com.demo.cs.study.clones.HavingDinner@34a245ab
哈士奇-->今天晚上要吃土豆雞塊  引用地址:com.demo.cs.study.clones.HavingDinner@7cc355be
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蠢络,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子迟蜜,更是在濱河造成了極大的恐慌刹孔,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件娜睛,死亡現(xiàn)場離奇詭異髓霞,居然都是意外死亡,警方通過查閱死者的電腦和手機畦戒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進店門方库,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人障斋,你說我怎么就攤上這事纵潦。” “怎么了垃环?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵邀层,是天一觀的道長。 經(jīng)常有香客問我遂庄,道長寥院,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任涛目,我火速辦了婚禮秸谢,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘霹肝。我一直安慰自己估蹄,他們只是感情好,可當我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布阿迈。 她就那樣靜靜地躺著,像睡著了一般轧叽。 火紅的嫁衣襯著肌膚如雪苗沧。 梳的紋絲不亂的頭發(fā)上刊棕,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天,我揣著相機與錄音待逞,去河邊找鬼甥角。 笑死,一個胖子當著我的面吹牛识樱,可吹牛的內(nèi)容都是我干的嗤无。 我是一名探鬼主播,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼怜庸,長吁一口氣:“原來是場噩夢啊……” “哼当犯!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起割疾,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤嚎卫,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后宏榕,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拓诸,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年麻昼,在試婚紗的時候發(fā)現(xiàn)自己被綠了奠支。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡抚芦,死狀恐怖倍谜,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情燕垃,我是刑警寧澤枢劝,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站卜壕,受9級特大地震影響您旁,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜轴捎,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一鹤盒、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧侦副,春花似錦侦锯、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春亲桥,著一層夾襖步出監(jiān)牢的瞬間洛心,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工题篷, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留词身,地道東北人。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓番枚,卻偏偏與公主長得像法严,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子葫笼,可洞房花燭夜當晚...
    茶點故事閱讀 42,792評論 2 345