【Java学习笔记】69:认识深拷贝,浅拷贝,clone()方法/序列化-反序列化实现深拷贝

版权声明:本文为博主原创学习笔记,如需转载请注明来源。 https://blog.csdn.net/SHU15121856/article/details/81582914

拷贝就是将一个对象的全部属性copy到另一个同类对象中。即使得某个对象以一个同类对象为原型,通过拷贝其全部属性来实现对象的拷贝。

同一个对象

注意!只要是拷贝,就一定是两个不同的对象。两个相同的对象引用相同,表示如下:
这里写图片描述
这不是拷贝,只是对同一个对象引用起了不同的变量名,绝不可将同一个对象视为拷贝。

浅拷贝

简述

浅拷贝就是只拷贝了对象的内容值,这个内容值包括基本类型值和引用值,表示如下:
这里写图片描述
表现在外,就是基本类型被重新拷贝了一份,而引用类型则只拷贝了引用值,但这种理解太过浅薄。就应该理解了Java的值和引用以后,将浅拷贝理解成不管什么属性全部都拷贝了值

可以注意下String的内容保存在常量池中,所以对其做修改只是改变了成员变量所引用的常量池的串,不会影响其它拷贝对象中的该成员。

实现浅拷贝

Object类的clone()方法即是实现了浅拷贝,另外也可以自己加个拷贝构造函数来实现浅拷贝。

一般只要继承Cloneable接口,在实现的clone()方法中用super关键字调用Object类的clone()方法,返回一个Object实例,然后强制转换成本类型并返回,注意处理异常即可。

package org.lzh;

//汉堡的面包片
public class Bread  implements Cloneable{
    private int facId;//面包片厂商id
    private String name;//面包片品牌名

    public Bread(){}

    //拷贝构造
    public Bread(Bread bread){
        this.facId=bread.facId;
        this.name=bread.name;
    }

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

这里为了方便就不加getter、setter和有参构造了。

深拷贝

深拷贝,就是对所有的属性都独立地复制一份。对于基本类型属性即是复制一份值(成员变量自会提供独立的空间),而对于引用类型的属性就是重新建立了一个一模一样的对象,引用值不再相同(不再指向同一个对象)。表示如下:
这里写图片描述

使用clone方法实现深拷贝

看一下要拷贝的对象的属性,对于基本类型和String类型,无需特殊处理,因为他们的修改都不会在拷贝对象之间产生影响。

对于其它的引用类型,则需要在调用了Objectclone()实现浅拷贝后再调用下它们的clone()方法得到拷贝的属性对象,传给拷贝对象。

package org.lzh;

import org.lzh.enumeration.Taste;

//各类汉堡产品记录
public class Burger  implements Cloneable{
    private int id;//汉堡id
    private String name;//汉堡产品名称
    private Taste taste;//口味
    private Double price;//价格,null表示为赠品
    private Bread bread;//汉堡使用的面包片

    @Override
    protected Object clone() throws CloneNotSupportedException {
        //先向上调用到Object的clone()方法做一下浅拷贝
        Object obj=super.clone();
        //转换成当前的汉堡类型
        Burger burger_clone= (Burger) obj;

        //此时int id和String name就拷贝好了,不用再考虑它们了
        //枚举类Taste,因为枚举永远是单例,也没必要去考虑了

        //考虑Double类型的price,做一下拷贝
        Double price_clone=new Double(price);//自动解包

        //考虑Bread类型的bread,做一下拷贝
        Bread bread_clone= (Bread) bread.clone();

        //深拷贝:设置拷贝了的属性对象
        burger_clone.price=price_clone;
        burger_clone.bread=bread_clone;

        return burger_clone;
    }
}

这里为了方便就不加getter、setter和有参构造了。

使用序列化-反序列化实现深拷贝

将对象序列化成字节序列,会将整个对象图进行序列化(除了transient修饰的属性),然后再反序列化到一个新对象里就实现了深拷贝。

让前面两个类Burger和Bread实现Serializable序列化接口,并提供getter、setter、全参constructor和toString()实现,以测试一下序列化和反序列化来实现深拷贝:

package org.lzh;

import org.lzh.enumeration.Taste;

import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //构造依赖对象以构造要序列化的burger对象
        Bread bread = new Bread(1, "小猫咪面包片");
        Burger burger = new Burger(1, "lzh辣堡", Taste.SPICY, new Double("15.5"), bread);

        //字节序列-输出流
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        //对象输出流,传入字节序列
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        //将要序列化的汉堡对象写入
        oos.writeObject(burger);
        //刷新
        oos.flush();

        //至此,burger对象已经序列化到[字节序列-输出流]中,下面做反序列化

        //字节序列-输入流,从字节序列输出流解成字节序列来构造
        //即将序列化后的对象读入到本[字节序列-输入流]中
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        //对象输入流,传入字节序列
        ObjectInputStream ois = new ObjectInputStream(bais);
        //读出其中的对象
        Burger burger_clone = (Burger) ois.readObject();

        //先看看是不是同一个对象
        System.out.println(burger == burger_clone);

        //再修改一下原来burger的属性
        Bread bread_other = new Bread(2, "小狗面包片");
        burger.setBread(bread_other);
        burger.setId(3);
        burger.setName("SB汉堡");
        burger.setTaste(Taste.NOTSPICY);
        burger.setPrice((double) 21);//自动装箱

        //输出一下,看看有没有对深拷贝后的burger_clone产生影响
        System.out.println(burger);
        System.out.println(burger_clone);
    }
}

输出:

false
id:3,name:SB汉堡,taste:NOTSPICY,price21.0,breadid:2,品牌名:小狗面包片
id:1,name:lzh辣堡,taste:SPICY,price15.5,breadid:1,品牌名:小猫咪面包片

补充

直接使用Java的clone()实现深拷贝会有很多坑(比如调用的属性的clone()能保证是深拷贝吗),使用序列化实现深拷贝是一种好的方式,也可以考虑使用一些其它工具,如Spring的BeanUtils工具。

猜你喜欢

转载自blog.csdn.net/SHU15121856/article/details/81582914