详解原型模式的浅复制(浅拷贝)和深复制(深拷贝)

前言

说起原型模式的浅复制和深复制, 我们都知道浅复制是对值类型的成员变量进行复制, 对引用类型的变量只是对引用进行复制, 实际上两个对象还是指向的同一实例。
而深复制不仅值类型的成员变量进行复制, 还对引用类型的成员变量申请存储空间, 让他成为一个新对象。
说的都没有错, 而远远知道这些还是不够的, 我们要明白其原理要去证明这两句话, 下面来说一下他们的实现过程!

浅复制

浅复制分为两步:

1. 实现Cloneable 接口, 我们可以进去看一下源码, 这个接口里一个方法都没有, 他的作用的就是在运行时通知虚拟机可以安全地在实现了此接口的类上使用clone()方法。在java虚拟机中,只有实现了这个接口的类才可以被拷贝。
2. Object 类为我们提供了一个clone方法, 所以我们重写clone方法来实现浅复制

代码

public class Sheep implements Cloneable {
    private String name;
    private int age;
    private String color;
    private Sheep friend;
	// 这里我get set tostring 方法就不写了
    @Override
    protected Object clone() {
        Sheep sheep = null;
        try {
            sheep = (Sheep) super.clone();
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
        return sheep;
    }
}

我们现在客户端执行这样的代码

    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep sheep = new Sheep("tom",1,"白色");
        sheep.setFriend(new Sheep("jack",2,"黑色"));
        Sheep sheep2 = (Sheep)sheep.clone();
        System.out.println(sheep.hashCode()); //输出结果为 356573597
        System.out.println(sheep2.hashCode()); //输出结果为 1735600054
    }

根据输出结果我们可知道, 两个对象并不是同一个, 这就说明我们值类型的成员变量是复制出来一份的, 接下来再看这样的一段代码

    public static void main(String[] args) throws CloneNotSupportedException {
        Sheep sheep = new Sheep("tom",1,"白色");
        sheep.setFriend(new Sheep("jack",2,"黑色"));
        Sheep sheep2 = (Sheep)sheep.clone();
        System.out.println(sheep.getFriend().hashCode());//输出结果为 356573597
        System.out.println(sheep2.getFriend().hashCode());//输出结果为 356573597
    }

根据输出结果来看, 两个对象中引用类型friend却是同一个, 由此可见浅复制对应用类型只是复制了引用, 并没有开辟新的存储空间。

深复制

实现深复制有两种方式:

  • 方式一: 使用clone方法 (需要实现Cloneable接口)
  • 方式一: 通过对象的序列化实现 (需要实现Serializable接口) 推荐使用

方式一

代码

public class DeepProtoType implements Serializable, Cloneable {
    private String name;
    private DeepCloneableTarget deepCloneableTarget;
	//get set 方法这里就不写了
    //深拷贝- 方式1 使用clone方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Object deep = null;
        //完成对基本数据类型和String的克隆
        deep = super.clone();
        //对引用类型的属性,进行单独处理
        DeepProtoType deepProtoType = (DeepProtoType) deep;
        deepProtoType.deepCloneableTarget = (DeepCloneableTarget) deepCloneableTarget.clone();
        return deep;
    }
}

这个类是为了辅助现实的

public class DeepCloneableTarget implements Serializable,Cloneable {
    private String cloneName;
    public DeepCloneableTarget(String cloneName) {
        this.cloneName = cloneName;
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

客户端代码

    public static void main(String[] args) throws CloneNotSupportedException {
        DeepProtoType p = new DeepProtoType();
        p.setName("小明");
        p.setDeepCloneableTarget(new DeepCloneableTarget("小红"));
        //方式一 完成深拷贝
        DeepProtoType p2 = (DeepProtoType) p.clone();
        System.out.println(p.getDeepCloneableTarget().hashCode()); //输出结果356573597
        System.out.println(p2.getDeepCloneableTarget().hashCode()); //输出结果1735600054
    }

根据输出结果可见, 两个对象的引用类型的成员变量不是相同的, 而是一个新的对象, 可证明深复制成功

方式二

代码

public class DeepProtoType implements Serializable, Cloneable {
    private String name;
    private DeepCloneableTarget deepCloneableTarget;
	//get set 方法这里就不写了
	// 深拷贝 -方式2 通过对象的序列化实现(推荐)
    public Object deepClone() {
        //创建流对象
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        try {
            //序列化
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            //反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            DeepProtoType o = (DeepProtoType) ois.readObject();
            return o;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            try {
                bos.close();
                oos.close();
                bis.close();
                ois.close();
            } catch (Exception e2) {
                System.out.println(e2.getMessage());
            }
        }
    }
}

客户端代码, 使用我们写好的deepClone方法区复制对象

    public static void main(String[] args) throws CloneNotSupportedException {
        DeepProtoType p = new DeepProtoType();
        p.setName("小明");
        p.setDeepCloneableTarget(new DeepCloneableTarget("小红"));
        //方式二 完成深拷贝
        DeepProtoType p2 = (DeepProtoType) p.deepClone();
        System.out.println(p.getDeepCloneableTarget().hashCode()); //输出结果621009875
        System.out.println(p2.getDeepCloneableTarget().hashCode()); //输出结果2065951873
    }

根据输出结果可知, 两个对象的引用类型的成员变量是不同的, 可证明深复制成功

总结

两种方式实现深复制, 我比较推荐使用第二种, 为什么呢, 如果一个类的引用类型很多, 第一种方式则需要一个一个处理, 第二种方式则是一次性处理了, 第二种方式还是比较方便一点。
如有对本文有疑惑的地方欢迎留言一起学习, 如果解决了你的疑问就点了赞支持一下吧!

你可能感兴趣的:(【Java】)