深拷贝和浅拷贝


Java中的对象拷贝主要分为:引用拷贝,浅拷贝(Shallow Copy)、深拷贝(Deep Copy)。

1、引用拷贝

并没有创建一个新的对象,两个对象都指向同一个引用地址。
对于对象内的数据,不管是基本类型还是引用类型的修改,都会影响到另一个对象。

2、浅拷贝

创建一个新对象,并对对象内的每个数据进行拷贝。

  • 数据属性是基本类型或String,拷贝的就是基本类型或String的值;
  • 数据属性是引用类型,拷贝的就是引用(即内存地址),如果内存地址内的值发生改变,就会影响到另一个对象。

实现对象拷贝的类,必须实现Cloneable接口,并覆写clone()方法。

必须重写Object中的clone()方法,要重写clone()方法,就必须实现Cloneable接口
如果重写了Object中的clone()方法,没有实现Cloneable接口,会报错java.lang.CloneNotSupportedException
protected native Object clone() throws CloneNotSupportedException;
3、深拷贝

创建一个新对象,并对对象内的每个数据进行拷贝。
如果数据类型是引用类型,则继续进行浅拷贝(本质是创建新对象,赋值),直到所有引用类型都是新创建的对象。
深拷贝实现方式:层层clone()实现深拷贝,序列化实现深拷贝

3.1、层层clone()实现深拷贝

对象的所有引用类型,都要进行引用类型的再次clone(),赋给对象

3.2、序列化实现深拷贝

对象的所有引用类型,都要实现Serializable接口

举例:

  • Person类持有三个变量(String类型,基本类型,引用类型),String name,int age,Address address

Person类

public class Person implements Cloneable, Serializable {

    //姓名
    private String name;
    // 年龄
    private int age;
    // 联系地址
    private Address address;

    public Person(String name, int age, Address address) {
        this.name = name;
        this.age = age;
        this.address = address;
    }
    
	//省略get、set、toString方法

    /**
     * 如果一个对象内部只有基本数据类型,则 clone() 方法获取到的就是这个对象的深拷贝。
     * 而如果其内部还有引用数据类型,那用 clone() 方法就是一次浅拷贝的操作。
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }

    /**
     * 对对象内的每一级引用类型(除String),做浅拷贝,即为深拷贝
     */
    public Object deepClone() throws CloneNotSupportedException {
        Person person = (Person) super.clone();
        Address address = (Address) getAddress().clone();
        person.setAddress(address);
        return person;
    }

    /**
     * 利用串行化来做深复制
     */
    public Object deepCloneBySerialize() {
        try {
            //写入对象
            ByteArrayOutputStream bo = new ByteArrayOutputStream();
            ObjectOutputStream os = new ObjectOutputStream(bo);
            os.writeObject(this);
            //读取对象
            ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
            ObjectInputStream oi = new ObjectInputStream(bi);
            return (oi.readObject());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }
}

Address类

public class Address implements Cloneable, Serializable {
    private String name;

    public Address(String name) {
        this.name = name;
    }

	//省略get、set、toString方法

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

测试Demo

public class CloneDemo {

    public static void main(String[] args) throws CloneNotSupportedException {

        //引用拷贝
        System.out.println("-------- 引用拷贝 ----------");
        Person p = new Person("本人",18, new Address("湖南"));
        Person p1 = p;
        System.out.println(p);
        System.out.println(p1);
        System.out.println(p == p1);
        p1.setName("本人copy");
        p1.setAge(666);
        p1.getAddress().setName("上海");
        System.out.println(p);
        System.out.println(p1);

        //浅拷贝
        System.out.println();
        System.out.println("-------- 浅拷贝 ----------");
        p = new Person("本人",18, new Address("湖南"));
        p1 = (Person)p.clone();
        System.out.println(p);
        System.out.println(p1);
        System.out.println("--------convert--");
        p1.setName("本人copy");
        p1.setAge(666);
        p1.getAddress().setName("上海");
        System.out.println(p);
        System.out.println(p1);

        //深拷贝实现方式1
        System.out.println();
        System.out.println("-------- 深拷贝 ----------");
        p = new Person("本人",18, new Address("湖南"));
        p1 = (Person)p.deepClone();
        System.out.println(p);
        System.out.println(p1);
        System.out.println("--------convert--");
        p1.setName("本人copy");
        p1.setAge(666);
        p1.getAddress().setName("上海");
        System.out.println(p);
        System.out.println(p1);

        //深拷贝实现方式2 serialize
        System.out.println();
        System.out.println("-------- 深拷贝 serialize ----------");
        p = new Person("本人",18, new Address("湖南"));
        p1 = (Person)p.deepCloneBySerialize();
        System.out.println(p);
        System.out.println(p1);
        System.out.println("--------convert--");
        p1.setName("本人copy");
        p1.setAge(666);
        p1.getAddress().setName("上海");
        System.out.println(p);
        System.out.println(p1);
        System.out.println("-------- xxxxx ----------");
    }
}

测试结果:

-------- 引用拷贝 ----------
Person{name='本人', age=18, address=Student{name='湖南'}}
Person{name='本人', age=18, address=Student{name='湖南'}}
true
Person{name='本人copy', age=666, address=Student{name='上海'}}
Person{name='本人copy', age=666, address=Student{name='上海'}}

-------- 浅拷贝 ----------
Person{name='本人', age=18, address=Student{name='湖南'}}
Person{name='本人', age=18, address=Student{name='湖南'}}
--------convert--
Person{name='本人', age=18, address=Student{name='上海'}}
Person{name='本人copy', age=666, address=Student{name='上海'}}

-------- 深拷贝 ----------
Person{name='本人', age=18, address=Student{name='湖南'}}
Person{name='本人', age=18, address=Student{name='湖南'}}
--------convert--
Person{name='本人', age=18, address=Student{name='湖南'}}
Person{name='本人copy', age=666, address=Student{name='上海'}}

-------- 深拷贝 serialize ----------
Person{name='本人', age=18, address=Student{name='湖南'}}
Person{name='本人', age=18, address=Student{name='湖南'}}
--------convert--
Person{name='本人', age=18, address=Student{name='湖南'}}
Person{name='本人copy', age=666, address=Student{name='上海'}}
-------- xxxxx ----------

参考
Java漫谈-深拷贝与浅拷贝
Java 浅拷贝和深拷贝的理解和实现方式

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值