原型模式

原型模式(Prototype Pattern)是指原型实例指定创建对象的种类,并且通过拷贝这些 原型创建新的对象。

原型模式主要适用于以下场景:

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

数据内容完全一样,但实例不同

  1. 能够直接拷贝其实际内容的数据类型/只支持9种,八大基本数据类型+String
  2. 深拷贝,其它类型也能拷贝

简单克隆

/**
 * 原型接口
 */
public interface ProtoType {
    ProtoType clone();
}

/**
 * 具体需要克隆的对象
 */
public class ConcretePrototype implements ProtoType{
    private int age;
    private String name;
    private List hobbies;

    @Override
    public ProtoType clone() {
        ConcretePrototype concretePrototype = new ConcretePrototype();
        concretePrototype.setAge(this.age);
        concretePrototype.setHobbies(this.hobbies);
        concretePrototype.setName(this.name);
        return concretePrototype;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public List getHobbies() {
        return hobbies;
    }

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


public class Client {
    private ProtoType protoType;
    public Client(ProtoType protoType){
        this.protoType= protoType;
    }
    public ProtoType startClone(){
        return protoType.clone();
    }
}
测试

public class Test {
    public static void main(String[] args){
        ConcretePrototype concretePrototype = new ConcretePrototype();
        concretePrototype.setAge(11);
        concretePrototype.setName("pro");
        ArrayList<Object> arrayList = new ArrayList<>();
        arrayList.add(1);
        concretePrototype.setHobbies(arrayList);
        System.out.println(concretePrototype);

        Client client = new Client(concretePrototype);
        ConcretePrototype protoType = (ConcretePrototype) client.startClone();
        System.out.println(protoType);
        System.out.println("克隆对象中的引用类型地址值:" + concretePrototype.getHobbies());
        System.out.println("原对象中的引用类型地址值:" + protoType.getHobbies());
        System.out.println("克隆对象中的hobbies:" + Arrays.asList(concretePrototype.getHobbies()));
        System.out.println("原对象中的hobbies:" + Arrays.asList(protoType.getHobbies()));
        System.out.println("对象地址比较:"+(protoType.getHobbies() == concretePrototype.getHobbies()));

        ArrayList<Object> hobbies = (ArrayList<Object>) concretePrototype.getHobbies();
        hobbies.add(2);
        concretePrototype.setHobbies(hobbies);
        System.out.println("克隆对象中的hobbies:" + Arrays.asList(concretePrototype.getHobbies()));
        System.out.println("原对象中的hobbies:" + Arrays.asList(protoType.getHobbies()));
//        com.vip.prototype.simple.ConcretePrototype@4b67cf4d
//        com.vip.prototype.simple.ConcretePrototype@7ea987ac
//        克隆对象中的引用类型地址值:[1]
//        原对象中的引用类型地址值:[1]
//        克隆对象中的hobbies:[[1]]
//        原对象中的hobbies:[[1]]
//        对象地址比较:true
//        克隆对象中的hobbies:[[1, 2]]
//        原对象中的hobbies:[[1, 2]]

//        从测试结果看出 hobbies 的引用地址是相同的,意味着复制的不是值,而是引用的地址。
//        修改任意一个对象中的属性值,concretePrototype 和 protoType 的 hobbies 值都会改变。这就是我们常说的浅克隆。
//        只是完整 复制了值类型数据,没有赋值引用对象。
    }
}

简单实例:


package prototype.simple;

import java.util.ArrayList;
import java.util.Date;

/**
 * 克隆基于字节码
 * 底层是c语言写的
 * 不走构造方法
 */
public class ConcretePrototype implements Cloneable{
    private int age;
    private String name;
    public ArrayList<String> list = new ArrayList<>();
    private Date date;

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

    @Override
    protected Object clone() throws CloneNotSupportedException {
        ConcretePrototype prototype = null;

        //能够直接拷贝其实际内容的数据类型/只支持9种,八大基本数据类型+String
        prototype = (ConcretePrototype) super.clone();
        //手动克隆
        prototype.list = (ArrayList<String>) list.clone();

        return prototype;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

package prototype.simple;

import java.util.Date;

/**
 * 原型模式
 * 数据内容完全一样,但实例不同
 * 1. 能够直接拷贝其实际内容的数据类型/只支持9种,八大基本数据类型+String
 * 2. 深拷贝,其它类型也能拷贝
 */
public class Test {
    public static void main(String[] args){
        ConcretePrototype cp = new ConcretePrototype();
        cp.setAge(11);
        cp.setName("abc");
        cp.list.add("hhhh");
        cp.setDate(new Date());
        try {
            ConcretePrototype clone = (ConcretePrototype) cp.clone();
            System.out.println(clone==cp);
            System.out.println(clone.list==cp.list);
            System.out.println(clone.getAge()+" "+clone.getName()+" "+clone.list.size());
            System.out.println(clone.getDate().getTime());
            System.out.println(cp.getDate().getTime());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

-------------------------------------------------

package prototype.prototype;

import java.util.Date;

/**
 * 鸣人
 */
public class MingRen {
    protected int height;
    protected Date date;

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }
}
package prototype.prototype;

import java.io.Serializable;

/**
 * 手里剑
 */
public class ShouLiJian implements Serializable {
    private float height = 10;

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

    public float getHeight() {
        return height;
    }
}
package prototype.prototype;

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

/**
 *
 */
public class YingFenSheng extends  MingRen implements Cloneable, Serializable {
    private ShouLiJian shouLiJian;

    public YingFenSheng() {
        System.out.println("构造执行");
        this.shouLiJian = new ShouLiJian();
        this.height = 170;
        this.date = new Date();
    }

    @Override
    protected Object clone(){
        //深度克隆
        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);
            YingFenSheng yingFenSheng = (YingFenSheng) ois.readObject();
            // 设置新的时间
            yingFenSheng.date = new Date();

            return yingFenSheng;
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if (bos!=null){
                    bos.close();
                }
                if (oos!=null){
                    oos.close();
                }
                if (bis!=null){
                    bis.close();
                }
                if (ois!=null){
                    ois.close();
                }
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        return null;
    }

    public void change(){
        YingFenSheng copy = (YingFenSheng)clone();
        System.out.println("mingren: "+this.date.getTime()+" "+copy.getHeight());
        System.out.println("copy: "+copy.getDate().getTime()+" "+copy.getHeight());
        System.out.println(this==copy);
        System.out.println(this.getShouLiJian()==copy.getShouLiJian());
        this.getShouLiJian().grow();
        System.out.println("------------");
        System.out.println("yuan ShouLiJian: "+this.getShouLiJian().getHeight());
        System.out.println("copy ShouLiJian: "+copy.getShouLiJian().getHeight());
    }

    public ShouLiJian getShouLiJian() {
        return shouLiJian;
    }

    public void setShouLiJian(ShouLiJian shouLiJian) {
        this.shouLiJian = shouLiJian;
    }
}
package prototype.prototype;

/**
 *
 */
public class Test {
    public static void main(String[] args){
        YingFenSheng yingFenSheng = new YingFenSheng();
        yingFenSheng.change();
    }
//mingren: 1582682313951 0
//copy: 1582682314078 0
//false
//false
//------------
//yuan ShouLiJian: 20.0
//copy ShouLiJian: 10.0
}

克隆破坏单例模式

如果我们克隆的目标的对象是单例对象,那意味着,深克隆就会破坏单例。实际上防止 克隆破坏单例解决思路非常简单,禁止深克隆便可。要么你我们的单例类不实现 Cloneable 接口;要么我们重写 clone()方法,在 clone 方法中返回单例对象即可,具体 代码如下:

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

ArrayList的Cloneable 源码

 /**
     * Returns a shallow copy of this <tt>ArrayList</tt> instance.  (The
     * elements themselves are not copied.)
     *
     * @return a clone of this <tt>ArrayList</tt> instance
     */
    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);
        }
    }
原文地址:https://www.cnblogs.com/fly-book/p/10371532.html