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
相关文章
相关标签/搜索