[01][01][03] 原型模式详解

1. 定义

指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象

调用者不需要知道任何创建细节,不调用构造函数

2. 适用场景

  • 类初始化消耗资源较多
  • new产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)
  • 构造函数比较复杂
  • 循环体重生产大量对象时
  • Spring中的scope就是使用的原型模式

3. 分类

  • 浅克隆: 指拷贝对象时仅仅copy对象本身和对象中的基本变量,而不拷贝对象包含的引用指向的对象
  • 深克隆: 不仅copy对象本身,而且copy对象包含的引用指向的所有对象

4. 浅克隆实现

4.1 克隆方法接口

package com.zhunongyun.toalibaba.designpatterns.prototype.simelp;

public interface Prototype {

    Prototype clone();
}

4.2 待克隆类

package com.zhunongyun.toalibaba.designpatterns.prototype.simelp;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class ConcretePrototypeA implements Prototype {
    /**
     * 年龄
     */
    private int age;

    /**
     * 姓名
     */
    private String name;

    /**
     * 兴趣爱好
     */
    private List hobbies;


    @Override
    public Prototype clone() {
        return new ConcretePrototypeA(this.age, this.name, this.hobbies);
    }
}

4.3 测试类

package com.zhunongyun.toalibaba.designpatterns.prototype.simelp;

import java.util.ArrayList;

public class PrototypeTest {

    public static void main(String[] args) {
        ConcretePrototypeA concretePrototypeA = new ConcretePrototypeA(18, "test_name", new ArrayList());

        ConcretePrototypeA copy = (ConcretePrototypeA) concretePrototypeA.clone();

        System.out.println("原对象与克隆对象内存地址对比, 地址是否相同:"
                + (concretePrototypeA == copy));

        System.out.println("原对象与克隆对象中引用类型内存地址对比, 地址是否相同:"
                + (concretePrototypeA.getHobbies() == copy.getHobbies()));
    }
}

4.4 总结

  • 浅克隆对于基本数据类型和String类型字段会重新申请内存复制数据,克隆对象会指向新的内存地址
  • 浅克隆对于引用类型的字段不会重新申请内存,而是把字段的内存地址指向之前原对象字段的内存地址

5. 深克隆

定义一个孙悟空类, 拔一根毫毛吹出千万个猴子, 每个猴子都有属于自己的金箍棒

通过字节码实现深克隆

5.1 待克隆类

package com.zhunongyun.toalibaba.designpatterns.prototype.deep;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.io.Serializable;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class JinGuBang implements Serializable {

    private float height = 100;

    private float wide = 10;

    public void big() {
        this.height *= 2;
        this.wide *= 2;
    }

    public void small() {
        this.height /= 2;
        this.wide /= 2;
    }
}
package com.zhunongyun.toalibaba.designpatterns.prototype.deep;

import lombok.Data;
import org.apache.commons.io.IOUtils;

import java.io.*;
import java.util.Date;

@Data
public class QiTianDaSheng implements Cloneable, Serializable {

    private JinGuBang jinGuBang;

    private int height;

    private int weight;

    private Date birthday;

    public QiTianDaSheng() {
        this.birthday = new Date();
        this.jinGuBang = new JinGuBang();
    }

    @Override
    protected Object clone() {
        return this.deepClone();
    }

    public Object deepClone() {
        ByteArrayOutputStream byteArrayOutputStream = null;
        ObjectOutputStream objectOutputStream = null;

        ByteArrayInputStream byteArrayInputStream = null;
        ObjectInputStream objectInputStream = null;

        // 内存中完成操作,对象读写,是通过字节码直接操作
        // 序列化操作类似
        try {
            byteArrayOutputStream = new ByteArrayOutputStream();
            objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
            objectOutputStream.writeObject(this);

            byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
            objectInputStream = new ObjectInputStream(byteArrayInputStream);

            // 完整的新的对象, new出来一个新的对象
            QiTianDaSheng copy = (QiTianDaSheng) objectInputStream.readObject();
            copy.setBirthday(new Date());

            return copy;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        } finally {
            IOUtils.closeQuietly(objectInputStream);
            IOUtils.closeQuietly(byteArrayInputStream);
            IOUtils.closeQuietly(objectOutputStream);
            IOUtils.closeQuietly(byteArrayOutputStream);
        }
    }
}

5.2 测试类

package com.zhunongyun.toalibaba.designpatterns.prototype.deep;

public class DeepCloneTest {
    public static void main(String[] args) {
        QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();

        QiTianDaSheng clone = (QiTianDaSheng) qiTianDaSheng.clone();

        System.out.println("深克隆,对象中的引用类型字段JinGuBang,内存地址是否相同:"
                + (qiTianDaSheng.getJinGuBang() == clone.getJinGuBang()));
    }
}

6. 源码分析

在ArrayList中存在clone方法, 但属于浅克隆

public Object clone() {
    try {
        ArrayList<?> v = (ArrayList<?>) super.clone();
        v.elementData = Arrays.copyOf(elementData, size);
        v.modCount = 0;
        return v;
    } catch (CloneNotSupportedException e) {
        // this shouldn't happen, since we are Cloneable
        throw new InternalError(e);
    }
}

7. Cloneable的使用

Cloneable是标记型的接口,它们内部都没有方法和属性,实现Cloneable来表示该对象能被克隆,能使用Object.clone()方法
如果没有实现Cloneable的类对象调用clone()就会抛出CloneNotSupportedException

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

猜你喜欢

转载自blog.csdn.net/csharpqiuqiu/article/details/100057161