Java中的深拷贝与浅拷贝

目录

一、浅拷贝

二、深拷贝

1、基本介绍  

 2、深拷贝的实现方式

(1)嵌套clone方法

 (2)序列化方式

三、总结


一、浅拷贝

        浅拷贝由对象实现cloneable接口和重写clone方法,然后调用一次内部不做改写的clone方法克隆出一个对象,如果源对象内部存在引用类型的成员变量,那么就说该克隆是浅克隆,即对于引用类型属性,只克隆引用,两个对象的引用指向同一块内存地址,即同一个对象。

 示例代码:

class Friend{
    private String name;
    private String home;

    public Friend(String name, String home) {
        this.name = name;
        this.home = home;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getHome() {
        return home;
    }

    public void setHome(String home) {
        this.home = home;
    }

    @Override
    public String toString() {
        return "Friend{" +
                "name='" + name + '\'' +
                ", home='" + home + '\'' +
                '}';
    }
}

class Person implements Cloneable{
    private String name;
    private Integer age;
    private Friend friend;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Friend getFriend() {
        return friend;
    }

    public void setFriend(Friend friend) {
        this.friend = friend;
    }

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

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

public class BaseOnClone {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person("张三", 18);
        person1.setFriend(new Friend("f1", "西安"));
        System.out.println("初始化的person:");
        System.out.println(person1);
        Person clonePerson = (Person) person1.clone();
        System.out.println("克隆出来的person:");
        System.out.println(clonePerson);
        clonePerson.setName("tom");
        clonePerson.setAge(20);
        clonePerson.getFriend().setHome("延安");
        System.out.println("修改克隆出来的person:");
        System.out.println(clonePerson);
        System.out.println("修改克隆出来的person之后,原来的person:");
        System.out.println(person1);
    }
}

        执行结果如下:

         从执行结果来看,对于克隆之后的clonePerson对象,当修改了它的age属性的值时,clonePerson的age的变化并不会影响到person1的age属性;当修改clonePerson对象的friend属性的home值时,person1也受到了影响。

        这是因为通过浅拷贝复制对象时仅仅只是复制对象本身,包括基本属性,但该对象的属性引用其他对象时,该引用对象不会被复制,其拷贝出来的对象和原对象中属性引用的对象是同一个。因此可以使用深拷贝达到完全复制的目的,使得原对象和拷贝出来的对象之间互不影响。

二、深拷贝

1、基本介绍  

        进行深拷贝时,基本数据类型变量和引用类型变量指向的对象都会被复制,即针对引用类型的成员变量真正的复制一份,重新开辟空间保存,这样两个引用类型属性互不影响。

 2、深拷贝的实现方式

(1)嵌套clone方法

        该方法给引用数据类型的属性也实现Cloneable接口,然后重写clone方法。以上面的浅拷贝例子为例:只需要在Friend中也实现Cloneable接口,然后重写clone()方法,再对Person中的clone()方法进行修改即可。

1)在Friend中也实现Cloneable接口,然后重写clone()方法

class Friend implements Cloneable{
    private String name;
    private String home;

    public Friend(String name, String home) {
        this.name = name;
        this.home = home;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getHome() {
        return home;
    }

    public void setHome(String home) {
        this.home = home;
    }

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

    @Override
    public String toString() {
        return "Friend{" +
                "name='" + name + '\'' +
                ", home='" + home + '\'' +
                '}';
    }
}

2) 对Person中的clone()方法进行修改

class Person implements Cloneable{
    private String name;
    private Integer age;
    private Friend friend;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Friend getFriend() {
        return friend;
    }

    public void setFriend(Friend friend) {
        this.friend = friend;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        person.friend = (Friend) friend.clone();
        return person;
    }

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

3)完整代码

class Friend implements Cloneable{
    private String name;
    private String home;

    public Friend(String name, String home) {
        this.name = name;
        this.home = home;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getHome() {
        return home;
    }

    public void setHome(String home) {
        this.home = home;
    }

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

    @Override
    public String toString() {
        return "Friend{" +
                "name='" + name + '\'' +
                ", home='" + home + '\'' +
                '}';
    }
}

class Person implements Cloneable{
    private String name;
    private Integer age;
    private Friend friend;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Friend getFriend() {
        return friend;
    }

    public void setFriend(Friend friend) {
        this.friend = friend;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        person.friend = (Friend) friend.clone();
        return person;
    }

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

public class BaseOnClone {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person1 = new Person("张三", 18);
        person1.setFriend(new Friend("f1", "西安"));
        System.out.println("初始化的person:");
        System.out.println(person1);
        Person clonePerson = (Person) person1.clone();
        System.out.println("克隆出来的person:");
        System.out.println(clonePerson);
        clonePerson.setName("tom");
        clonePerson.setAge(20);
        clonePerson.getFriend().setHome("延安");
        System.out.println("修改克隆出来的person:");
        System.out.println(clonePerson);
        System.out.println("修改克隆出来的person之后,原来的person:");
        System.out.println(person1);
    }
}

         执行结果如下:

       

         从执行结果来看,在进行深拷贝的时候,无论是对基本数据类型 (age) 还是引用数据类型 (Friend) 进行修改,原对象 (Person) 都不会受到影响。 

 (2)序列化方式

        如果当类中的属性存在数组(数组不能实现Cloneable接口)或者属性之间的关系比较复杂时,上面的方法都不能很好的实现深克隆了。序列化的方式是让每个类都实现Serializable接口,然后通过序列化和反序列化操作达到深克隆的目的。步骤如下:

  • 使要序列化的对象和该对象的引用类型成员变量对象的类都实现Serializable接口,
  • 创建一个ByteArrayOutputStream内存数组输出流,创建一个ObjectOutputStream序列化流,并传入内存数组输出流,使用序列化流的writeobject方法将要序列化的对象写入内部数组中
  • 创建一个ByteArrayInputStream内存数组读取流,传入一个读取数据的数组,这个数组通过内存数组输出流的toByteArray方法获得,这个数组里面的数据其实就是已经被序列化成二进制数据的对象。
  • 最后创建一个ObjectInputStream反序列化流,并传入内存数组读取流,使用反序列化流的readobject方法将数组中的对象的信息反序列化出来。由于反序列化出的对象就是一个新的对象,完成了深克隆。

        注意:序列化时通过定义一个private static final long serialVersionUID固定要被序列化对象的版本号,且静态的成员和transient关键字修饰的成员不能被序列化。

        代码如下:



import java.io.*;

class Friend implements Serializable {
    private String name;
    private String home;

    public Friend(String name, String home) {
        this.name = name;
        this.home = home;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getHome() {
        return home;
    }

    public void setHome(String home) {
        this.home = home;
    }

    @Override
    public String toString() {
        return "Friend{" +
                "name='" + name + '\'' +
                ", home='" + home + '\'' +
                '}';
    }
}

class Person implements Serializable{
    private String name;
    private Integer age;
    private Friend friend;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Friend getFriend() {
        return friend;
    }

    public void setFriend(Friend friend) {
        this.friend = friend;
    }

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

public class BaseOnClone {
    public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
        Person person = new Person("张三", 18);
        person.setFriend(new Friend("f1", "西安"));
        System.out.println("初始化的person:");
        System.out.println(person);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(person);

        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        Person clonePerson = (Person) ois.readObject();

        System.out.println("克隆出来的person:");
        System.out.println(clonePerson);
        clonePerson.setName("tom");
        clonePerson.setAge(24);
        clonePerson.getFriend().setHome("长春");
        System.out.println("修改克隆出来的person:");
        System.out.println(clonePerson);
        System.out.println("修改克隆出来的person之后,原来的person:");
        System.out.println(person);
    }
}

        执行结果如下:

         

        从执行结果来看,序列化的方式也实现了深拷贝。在使用序列化实现深拷贝时,将对象进行序列化之后写入流中,然后通过反序列化读取流,生成新对象,而新生成的对象和原对象之间互不影响。

三、总结

        对于浅拷贝而言,在复制对象时仅仅复制对象本身,包括基本属性和引用类型,拷贝出来的对象和原对象中属性所引用的对象是同一个。

        对于深拷贝而言,复制之后的对象和原对象之间互不影响。

  • 2
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值