一. 类型

创建型模式

二. 定义

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

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

三. 适用场景

  1. 类初始化消耗资源较多
  2. new 产生的一个对象需要非常繁琐的过程(数据准备、访问权限等)
  3. 构造函数比较复杂
  4. 循环体中产生大量对象时

四. 优点

  1. 性能优良, java 自带的原型模式是基于内存二进制流的拷贝, 比直接 new 一个对象性能上提升了许多
  2. 可以使用深克隆方式保存对象的状态, 使用原型模式将对象复制一份将其状态保存起来, 简化了创建过程.

五. 缺点

  1. 必须配备克隆(或者可拷贝)的方法
  2. 当对已有的类进行改造的时候, 需要修改代码, 违反了开闭原则
  3. 深拷贝、浅拷贝需要运用得当

六. 原型模式写法

浅拷贝

/**
 * 类实现了Cloneable接口来指示Object.clone()方法,该方法可以合法地对类的实例进行字段对字段的复制。  
 * 在一个没有实现Cloneable接口的实例上调用Object的clone方法会导致抛出CloneNotSupportedException异常。  
 */
@Data
public class ConcretePrototype implements Cloneable {

    private int age;
    private String name;
    private List<String> hobbies;

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

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

使用案例

public static void main(String[] args) {
        //创建原型对象
        ConcretePrototype prototype = new ConcretePrototype();
        prototype.setAge(18);
        prototype.setName("Tom");
        List<String> hobbies = new ArrayList<String>();
        hobbies.add("书法");
        hobbies.add("美术");
        prototype.setHobbies(hobbies);

        //拷贝原型对象
        ConcretePrototype cloneType = prototype.clone();
        cloneType.getHobbies().add("技术控");


        System.out.println("原型对象:" + prototype);
        System.out.println("克隆对象:" + cloneType);
        System.out.println(prototype == cloneType);


        System.out.println("原型对象的爱好:" + prototype.getHobbies());
        System.out.println("克隆对象的爱好:" + cloneType.getHobbies());
        System.out.println(prototype.getHobbies() == cloneType.getHobbies());

    }
执行结果
--------------------------------------------------------------------------------------------
原型对象:ConcretePrototype{age=18, name='Tom', hobbies=[书法, 美术, 技术控]}
克隆对象:ConcretePrototype{age=18, name='Tom', hobbies=[书法, 美术, 技术控]}
false
原型对象的爱好:[书法, 美术, 技术控]
克隆对象的爱好:[书法, 美术, 技术控]
true
--------------------------------------------------------------------------------------------

*结论

浅拷贝时引用对象是通过 JDK 中的字节流去完成复制, JDK 底层有一个机制, 如果是一个类已经存在的时候不会去重新加载, 就会进行一个值与值的一个简单复制, 相当于拷贝的是引用, 就会造成以上修改克隆对象中的值时, 原对象也已经被修改

深拷贝

/**
 * 深拷贝
 */
@Data
public class ConcretePrototype implements Cloneable,Serializable {

    private int age;
    private String name;
    private List<String> hobbies;

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

    /**
     * 可以强转成ArrayList, ArrayList 的clone方法就是使用的深拷贝
     */
    public ConcretePrototype deepCloneHobbies(){
        try {
            ConcretePrototype result = (ConcretePrototype)super.clone();
            result.hobbies = (List)((ArrayList)result.hobbies).clone();
            return result;
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 序列化和反序列化 可以破坏单例, 可以利用这一点来返回一个新对象
     */
    public ConcretePrototype deepClone(){
        try {
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(bos);
            oos.writeObject(this);

            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bis);

            return (ConcretePrototype)ois.readObject();
        }catch (Exception e){
            e.printStackTrace();
            return null;
        }
    }

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

使用案例

public static void main(String[] args) {
    //创建原型对象
    ConcretePrototype prototype = new ConcretePrototype();
    prototype.setAge(18);
    prototype.setName("Tom");
    List<String> hobbies = new ArrayList<String>();
    hobbies.add("书法");
    hobbies.add("美术");
    prototype.setHobbies(hobbies);

    //拷贝原型对象
    ConcretePrototype cloneType = prototype.deepCloneHobbies();
    // ConcretePrototype cloneType = prototype.deepClone();
    cloneType.getHobbies().add("技术控");

    System.out.println("原型对象:" + prototype);
    System.out.println("克隆对象:" + cloneType);
    System.out.println(prototype == cloneType);


    System.out.println("原型对象的爱好:" + prototype.getHobbies());
    System.out.println("克隆对象的爱好:" + cloneType.getHobbies());
    System.out.println(prototype.getHobbies() == cloneType.getHobbies());
}
执行结果
--------------------------------------------------------------------------------------------
原型对象:ConcretePrototype{age=18, name='Tom', hobbies=[书法, 美术]}
克隆对象:ConcretePrototype{age=18, name='Tom', hobbies=[书法, 美术, 技术控]}
false
原型对象的爱好:[书法, 美术]
克隆对象的爱好:[书法, 美术, 技术控]
false
--------------------------------------------------------------------------------------------

*结论

以上深拷贝两种方式

  1. 参见 public ConcretePrototype deepCloneHobbies() 方法
    可以强转成ArrayList, ArrayList 的clone方法就是使用的深拷贝
  2. 参见 public ConcretePrototype deepClone() 方法
    序列化和反序列化 可以破坏单例, 可以利用这一点来返回一个新对象
最后修改:2022 年 06 月 23 日
如果觉得我的文章对你有用,请点个赞吧~