Java 对象的浅拷贝和深拷贝

一、概述

对象拷贝(Object Copy)就是将一个对象的属性拷贝到另一个有着相同类类型的对象中去。在程序中拷贝对象是很常见的,主要是为了在新的上下文环境中复用对象的部分或全部数据。Java中有三种类型的对象拷贝:浅拷贝(Shallow Copy)、深拷贝(Deep Copy)。:

  • 浅拷贝: 浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值;如果属性是内存地址(引用类型),拷贝的就是内存地址 ,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。
  • 深拷贝:深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。
  • 无论是浅拷贝还是深拷贝,都需要实现 clone() 方法,来完成操作。

二、浅拷贝

在这里插入图片描述

public class TestDemo{
    public static void main(String[] args) {

        Friend friend = new Friend("Ace","man",18);
        // 原始对象
        Person person = new Person("Jeffrey",18, friend);
        System.out.println("原始对象: " + person);

         // 拷贝对象
        Person clonedPperson = (Person)person.clone();
        System.out.println("拷贝对象: " + clonedPperson);

        //原始对象和拷贝对象是否一样
        System.out.println("原始对象和拷贝对象是否一样: " + (person == clonedPperson));

        //修改基本类型
        person.setAge(20);
        System.out.println("修改基本类型后的对比:");
        System.out.println("原始对象: " + person);
        System.out.println("拷贝对象: " + clonedPperson);
        //修改引用类型
        person.getFriend().setAge(30);
        System.out.println("修改引用类型后的对比:");
        System.out.println("原始对象: " + person);
        System.out.println("拷贝对象: " + clonedPperson);
    }
}

class Person implements Cloneable{

    private String name;
    private int age;
    private Friend friend;
    public Person(String name, int age ,Friend friend) {
        this.name = name;
        this.age = age;
        this.friend = friend;
    }

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

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

    public Friend getFriend() {
        return friend;
    }

    @Override
    public Object clone(){
        try {
            return super.clone();
        } catch (CloneNotSupportedException e) {
           return null ;
        }
    }

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


class Friend{
    private String name;
    private String sex;
    private int age;
    public Friend(String name,String sex,int age) {
        this.name = name;
        this.sex = sex;
        this.age = age;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

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

    public String toString() {
        return "{ Friend:" +  "name=" + name + ", sex=" + sex + ", age=" + age + " }";
    }

}

输出结果:

原始对象: Person={ name=Jeffrey, age=18, friend={ Friend:name=Ace, sex=man, age=18 } }
拷贝对象: Person={ name=Jeffrey, age=18, friend={ Friend:name=Ace, sex=man, age=18 } }
原始对象和拷贝对象是否一样: false
修改基本类型后的对比:
原始对象: Person={ name=Jeffrey, age=20, friend={ Friend:name=Ace, sex=man, age=18 } }
拷贝对象: Person={ name=Jeffrey, age=18, friend={ Friend:name=Ace, sex=man, age=18 } }
修改引用类型后的对比:
原始对象: Person={ name=Jeffrey, age=20, friend={ Friend:name=Ace, sex=man, age=30 } }
拷贝对象: Person={ name=Jeffrey, age=18, friend={ Friend:name=Ace, sex=man, age=30 } }

根据结果来看,通过Clonable接口并重写Object类的clone()方法来赋值的对象,值类型的赋值不会改变,引用类型会改变,印证了只是赋值栈上面的数据和引用对象地址,最终指向堆的内存地址一致。

三、深拷贝

在这里插入图片描述
常用的方案有两种:

  • 继续利用 clone()方法,对其内的引用类型的变量,再进行一次clone()(Friend实现),或者在clone()创建新的引用变量赋值和新的对象
  • 序列化(serialization)这个对象,再反序列化回来,就可以得到这个新的对象。

1、clone

修改Pesron类,新创建一个对象返回:

  @Override
    public Object clone(){
        // 深拷贝,创建拷贝类的一个新对象,这样就和原始对象相互独立
        return new Person(name, age,new Friend(friend.name,friend.sex,friend.age));
    }

2、序列化实现

具体参考Java 序列化,只是把FileOutputStream改成ByteArrayOutputStream。因为主要是FileOutputStream作用于文件,后者作用于byte[]。

参考Java 深拷贝和浅拷贝细说 Java 的深拷贝和浅拷贝

发布了132 篇原创文章 · 获赞 150 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_35988274/article/details/102861345