(二)Java架构师成长之路-设计模式:原型模式,浅克隆和深克隆

一.什么是原型模式

原型模式是指原型对象指定创建对象的种类,并通过拷贝这些原型的方式创建新的对象。

调用者不需要知道创建对象的细节,不调用构造函数就能创建对象。

属于创建型模式。

目的是为了减少创建对象的复杂度,提升系统性能,一般是基于二进制流的拷贝。

二.原型模式的通用写法

我们先来看一下原型模式的通用写法

先创建一个顶层接口

public interface IProtoType<T> {

    // 第一种方法
    T clone();

    // 第二种方法
    T clone(T object);
}

再创建一个类来实现这个接口

public class CommonProtoType implements IProtoType{

    private Integer id;

    private String name;

    private Integer age;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    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;
    }

    // 第一种方式,需要每个字段进行赋值,适合字段比较少的情况
    @Override
    public CommonProtoType clone() {
        CommonProtoType commonProtoType = new CommonProtoType();
        commonProtoType.setId(this.getId());
        commonProtoType.setAge(this.getAge());
        commonProtoType.setName(this.getName());
        return commonProtoType;
    }

    // 第二种方式,利用反射为属性赋值,适合字段较多的情况
    @Override
    public Object clone(Object object) {
        try {
            Class clazz = object.getClass();
            Object returnValue = clazz.newInstance();
            Field[] fileds = clazz.getDeclaredFields();
            for (Field filed : fileds) {
                filed.setAccessible(true);
                filed.set(returnValue, filed.get(object));
            }
            return returnValue;
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }
}

三.原型模式的使用场景

1.new一个对象并且给属性赋值的过程非常繁琐

2.构造方法比较复杂

3.循环体内大量创建具有相同属性的对象

4.类初始化消耗资源较多

注意:克隆和new的区别:克隆不会走构造方法

四.浅克隆和深克隆

1.浅克隆

在拷贝引用数据类型的时候会拷贝引用的内存地址,而不拷贝具体的值(String除外,因为它的value是final的)。

先来看一个案例

创建一个浅克隆的类实现Cloneable接口,实现clone方法,直接调用父类的clone方法。Object类实现了Cloneable接口,是一个本地方法,默认是浅克隆

public class ShallowProtoType implements Cloneable {

    private Integer id;

    private String name;

    private Integer age;

    private List<String> hobbies;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    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 List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

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

    @Override
    public String toString() {
        return "ShallowProtoType{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", hobbies=" + hobbies +
                '}';
    }
}

创建测试类 

public class Test {

    public static void main(String[] args) {
        ShallowProtoType shallowProtoType = new ShallowProtoType();
        shallowProtoType.setId(1);
        List<String> hobbies = new LinkedList<String>() {
            {
                this.add("打篮球");
                this.add("打游戏");
            }
        };
        shallowProtoType.setHobbies(hobbies);
        shallowProtoType.setAge(18);
        shallowProtoType.setName("周杰伦");

        ShallowProtoType cloneType = shallowProtoType.clone();
        System.out.println("改变克隆对象的爱好前:");
        System.out.println("原型对象" + shallowProtoType);
        System.out.println("克隆对象" + cloneType);
        System.out.println("改变克隆对象的爱好后:");
        cloneType.getHobbies().add("唱歌");
        System.out.println("原型对象" + shallowProtoType);
        System.out.println("克隆对象" + cloneType);
    }
}

输出结果

可以看出,克隆对象的引用类型的属性改变之后,原型对象也跟着改变了。

2.深克隆

为了避免浅克隆带来的问题,由此引入深克隆的方式。

还记得我们之前讲过的序列化和反序列会破坏单例吗?这里就要利用这一点,在序列化和反序列的时候,不实现自己的readResolve方法,那么每次就会返回一个不同的实例。

创建一个深克隆的类,实现Serializable接口。

public class DeepProtoType implements Serializable {

    private Integer id;

    private String name;

    private Integer age;

    private List<String> hobbies;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    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 List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    public DeepProtoType deepClone() {
        try {
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(this);

            ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
            return (DeepProtoType) objectInputStream.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return null;
    }

    @Override
    public String toString() {
        return "DeepProtoType{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", hobbies=" + hobbies +
                '}';
    }
}

创建测试类

public class Test {

    public static void main(String[] args) {
        DeepProtoType deepProtoType = new DeepProtoType();
        deepProtoType.setId(1);
        List<String> hobbies = new LinkedList<String>() {
            {
                this.add("打篮球");
                this.add("打游戏");
            }
        };
        deepProtoType.setHobbies(hobbies);
        deepProtoType.setAge(18);
        deepProtoType.setName("周杰伦");

        DeepProtoType cloneType = deepProtoType.deepClone();
        System.out.println("改变克隆对象的爱好前:");
        System.out.println("原型对象" + deepProtoType);
        System.out.println("克隆对象" + cloneType);
        System.out.println("改变克隆对象的爱好后:");
        cloneType.getHobbies().add("唱歌");
        System.out.println("原型对象" + deepProtoType);
        System.out.println("克隆对象" + cloneType);
    }
}

输出结果

可以看出,深克隆完美解决了浅克隆带来的问题

除了序列化和反序列的方式外,可以利用json来实现深克隆,阿里和谷歌的json库都可以。

五.原型模式在源码中的应用

ArrayList、HashMap

这两个都是浅克隆,只要是通过实现Cloneable接口来实现克隆的都是浅克隆,深克隆一般要自己去实现。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值