java對象的淺克隆和深克隆

引言:

  在Object基類中,有一個方法叫clone,產生一個前期對象的克隆,克隆對象是原對象的拷貝,因爲引用類型的存在,有深克隆和淺克隆之分,若克隆對象中存在引用類型的屬性,深克隆會將此屬性徹底拷貝一份,而淺克隆僅僅是拷貝一份此屬性的引用。首先看一下容易犯的幾個小問題java

  • clone方法是Object類的,並非Cloneable接口的,Cloneable只是一個標記接口,標記接口是用用戶標記實現該接口的類具備某種該接口標記的功能,常見的標記接口有三個:Serializable、Cloneable、RandomAccess,沒有實現Cloneable接口,那麼調用clone方法就會爆出CloneNotSupportedException異常。
  • Object類中的clone方法是protected修飾的,這就代表咱們在子類中不重寫此方法,就在子類外沒法訪問,由於這個protected權限是僅僅能在Object所在的包和子類能訪問的,這也驗證了子類重寫父類方法權限修飾符能夠變大但不能變小的說法。
    protected native Object clone() throws CloneNotSupportedException;

     

  • 重寫clone方法,內部僅僅是調用了父類的clone方法,實際上是爲了擴大訪問權限,固然你能夠把protected改成public,之後再繼承就不用重寫了。固然只是淺克隆的clone函數,深克隆就須要修改了。
        @Override
        protected Object clone() throws CloneNotSupportedException {
            return super.clone();
        }
  • 屬性是String的狀況,String也是一個類,那String引用類型嗎?String的表現有的像基本類型,歸根到底就是由於String不可改變,克隆以後倆個引用指向同一個String,但當修改其中的一個,改的不是String的值,倒是新生成一個字符串,讓被修改的引用指向新的字符串。外表看起來就像基本類型同樣。

淺克隆:

  淺克隆就是引用類型的屬性沒法徹底複製,類User中包含成績屬性Mark,Mark是由Chinese和math等等組成的,淺克隆失敗的例子數組

class Mark{
    private int chinese;
    private int math;
    public Mark(int chinese, int math) {
        this.chinese = chinese;
        this.math = math;
    }

    public void setChinese(int chinese) {
        this.chinese = chinese;
    }

    public void setMath(int math) {
        this.math = math;
    }

    @Override
    public String toString() {
        return "Mark{" +
                "chinese=" + chinese +
                ", math=" + math +
                '}';
    }
}
public class User implements Cloneable{
    private String name;
    private int age;
    private Mark mark;

    public User(String name, int age,Mark mark) {
        this.name = name;
        this.age = age;
        this.mark = mark;
    }

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

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

    public static void main(String[] args) throws CloneNotSupportedException {
        Mark mark = new Mark(100,99);
        User user = new User("user",22,mark);
        User userClone = (User) user.clone();
        System.out.println("原user:"+user);
        System.out.println("克隆的user:"+userClone);
        //修改引用類型的mark屬性
        user.mark.setMath(60);
        System.out.println("修改後的原user:"+user);
        System.out.println("修改後的克隆user:"+userClone);
    }
}

  輸出結果爲:   dom

    原user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}
    克隆的user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}
    修改後的原user:User{name='user', age=22, mark=Mark{chinese=100, math=60}}
    修改後的克隆user:User{name='user', age=22, mark=Mark{chinese=100, math=60}}ide

  很清楚的看到user的mark更改後,被克隆的user也修改了。而要想不被影響,就須要深克隆了。函數

深克隆:

 方式一:clone函數的嵌套調用

  既然引用類型沒法被徹底克隆,那將引用類型也實現Cloneable接口重寫clone方法,在User類中的clone方法調用屬性的克隆方法,也就是方法的嵌套調用this

class Mark implements Cloneable{
    private int chinese;
    private int math;
    public Mark(int chinese, int math) {
        this.chinese = chinese;
        this.math = math;
    }
    public void setChinese(int chinese) {
        this.chinese = chinese;
    }
    public void setMath(int math) {
        this.math = math;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
    @Override
    public String toString() {
        return "Mark{" +
                "chinese=" + chinese +
                ", math=" + math +
                '}';
    }
}
public class User implements Cloneable{
    private String name;
    private int age;
    private Mark mark;

    public User(String name, int age,Mark mark) {
        this.name = name;
        this.age = age;
        this.mark = mark;
    }

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

    @Override
    protected Object clone() throws CloneNotSupportedException {
        User user = (User) super.clone();
        user.mark = (Mark) this.mark.clone();
        return user;
    }

    public static void main(String[] args) throws CloneNotSupportedException {
        Mark mark = new Mark(100,99);
        User user = new User("user",22,mark);
        User userClone = (User) user.clone();
        System.out.println("原user:"+user);
        System.out.println("克隆的user:"+userClone);
        //修改引用類型的mark屬性
        user.mark.setMath(60);
        System.out.println("修改後的原user:"+user);
        System.out.println("修改後的克隆user:"+userClone);
    }
}

  輸出結果爲: spa

    原user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}
    克隆的user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}
    修改後的原user:User{name='user', age=22, mark=Mark{chinese=100, math=60}}
    修改後的克隆user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}code

 方式二:序列化

  上一種方法已經足夠知足咱們的須要,可是若是類之間的關係不少,或者是有的屬性是數組呢,數組可沒法實現Cloneable接口(咱們能夠在clone方法中手動複製數組),可是每次都得手寫clone方法,很麻煩,而序列化方式只須要給每一個類都實現一個Serializable接口,也是標記接口,最後同序列化和反序列化操做達到克隆的目的(包括數組的複製)。序列化和反序列化的知識請參照下一篇對象

import java.io.*;
class Mark implements Serializable {
    private int chinese;
    private int math;
    public Mark(int chinese, int math) {
        this.chinese = chinese;
        this.math = math;
}
    public void setChinese(int chinese) {
        this.chinese = chinese;
    }
    public void setMath(int math) {
        this.math = math;
    }
    @Override
    public String toString() {
        return "Mark{" +
                "chinese=" + chinese +
                ", math=" + math +
                '}';
    }
}
public class User implements Serializable{
    private String name;
    private int age;
    private Mark mark;

    public User(String name, int age,Mark mark) {
        this.name = name;
        this.age = age;
        this.mark = mark;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", mark=" + mark +
                '}';
    }
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Mark mark = new Mark(100,99);
        User user = new User("user",22,mark);

        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        ObjectOutputStream oo = new ObjectOutputStream(bo);
        oo.writeObject(user);//序列化
        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi = new ObjectInputStream(bi);
        User userClone = (User) oi.readObject();//反序列化

        System.out.println("原user:"+user);
        System.out.println("克隆的user:"+userClone);
        user.mark.setMath(59);
        System.out.println("修改後的原user:"+user);
        System.out.println("修改後的克隆user:"+userClone);
    }
}

  輸出結果:    blog

    原user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}
    克隆的user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}
    修改後的原user:User{name='user', age=22, mark=Mark{chinese=100, math=60}}
    修改後的克隆user:User{name='user', age=22, mark=Mark{chinese=100, math=99}}

  帶數組屬性的克隆:

import java.io.*;
import java.util.Arrays;

public class User implements Serializable{
    private String name;
    private int age;
    private int[] arr;

    public User(String name, int age, int[] arr) {
        this.name = name;
        this.age = age;
        this.arr = arr;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", arr=" + Arrays.toString(arr) +
                '}';
    }
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        int[] arr = {1,2,3,4,5,6};
        User user = new User("user",22,arr);

        ByteArrayOutputStream bo = new ByteArrayOutputStream();
        ObjectOutputStream oo = new ObjectOutputStream(bo);
        oo.writeObject(user);//序列化
        ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
        ObjectInputStream oi = new ObjectInputStream(bi);
        User userClone = (User) oi.readObject();//反序列化

        System.out.println("原user:"+user);
        System.out.println("克隆的user:"+userClone);
        user.arr[1] = 9;
        System.out.println("修改後的原user:"+user);
        System.out.println("修改後的克隆user:"+userClone);
    }
}
View Code
相關文章
相關標籤/搜索