1.什么是原型模式?
原型模式提供了一种创建对象的模式,它是指用原型实例创建对象的种类,并且通过拷贝这些原型,创建新的对象。用一个很生动形象的例子:孙悟空拔出一根猴毛,变出其他和自己一模一样的小孙悟空,在这里,原型实例就是孙悟空,拔出的猴毛通过拷贝孙悟空的外表特征,变成了其他小孙悟空。
如何实现?
在java中通过实现 实现 Cloneable 接口,并且重写clone方法,在创建新对象时候,调用原型实例.clone()
2.简单的代码实现
克隆羊多莉大家一定听说过吧,那么就用多利羊来说明这个原型模式吧
首先创建出一个羊的实例对象,去实现Cloneable 接口
public class Sheep implements Cloneable{
    private String name;
    private int age;
    public Sheep(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Override
    protected Object clone(){
        Object clone = null;
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
        return clone;
    }
    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
测试一下原型模式创建对象
public class Test {
    public static void main(String[] args){ 
        Sheep sheep = new Sheep("Dolly", 1);
        Sheep duoli1 = (Sheep) sheep.clone();
        Sheep duoli2 = (Sheep) sheep.clone();
        Sheep duoli3 = (Sheep) sheep.clone();
        System.out.println("duoli1:"+duoli1+"hashcode:"+duoli1.hashCode());
        System.out.println("duoli2:"+duoli2+"hashcode:"+duoli2.hashCode());
        System.out.println("duoli3:"+duoli3+"hashcode:"+duoli3.hashCode());
    }
}

3浅拷贝VS深拷贝
3.1浅拷贝
基本数据类型的成员变量:将属性值复制一份给新的对象,是值传递
引用数据变量的成员变量:将该成员变量的地址值复制一份给新的对象,是地址传递,也就是说,不管在哪个对象中修改这个成员变量,都会影响到另一个对象中该成员变量的值。
浅拷贝默认的实现就是.clone()方法
下面就来进行一个测试,在Sheep类中新增一个person类,代表羊的主人
public class Person {
    private String name;
    public Person(String name) {
        this.name = name;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}
在Sheep类中增加上这个person

测试
public class Test {
    public static void main(String[] args){
        Person master = new Person("多莉的主人");
        Sheep sheep = new Sheep("Dolly", 1,master);
        Sheep duoli1 = (Sheep) sheep.clone();
        Sheep duoli2 = (Sheep) sheep.clone();
        Sheep duoli3 = (Sheep) sheep.clone();
        System.out.println("duoli1:"+duoli1+"hashcode:"+duoli1.hashCode()+"master:"+duoli1.getPerson().hashCode());
        System.out.println("duoli2:"+duoli2+"hashcode:"+duoli2.hashCode()+"master:"+duoli2.getPerson().hashCode());
        System.out.println("duoli3:"+duoli3+"hashcode:"+duoli3.hashCode()+"master:"+duoli2.getPerson().hashCode());
    }
}

3.2深拷贝
基本数据类型的成员变量:值传递
引用数据类型的成员变量:对整个对象进行拷贝,包括对象的引用类型,不再是拷贝地址了
clone()默认是浅拷贝,那么如何实现深拷贝呢?
方法一:重写clone方法
方法二:通过对象序列化实现
方式一实现起来非常简答,只需要Person也去实现Cloneable接口,并且重写clone方法,然后在修改Sheep的clone方法即可
Person的clone方法
@Override
    protected Object clone() {
        Object clone = null;
        try {
            clone = super.clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
        return clone;
    }
重写Sheep的clone方法
@Override
    protected Object clone(){
        Sheep clone = null;
        try {
            //转换为Sheep
            clone = (Sheep) super.clone();
            //person也克隆过来
            clone.setPerson((Person) person.clone());
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
        return clone;
    }
重新运行测试

方式二:通过序列化实现
①:实现序列化接口
Sheep和Person都要,然后不用再实现Cloneable接口了
public class Sheep implements Serializable 
②:自定义深拷贝方法
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);
            Sheep sheep = (Sheep) ois.readObject();
            return sheep;
        } 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());
            }
        }
    }
测试:
public class Test {
    public static void main(String[] args){
        Person master = new Person("多莉的主人");
        Sheep sheep = new Sheep("Dolly", 1,master);
        Sheep duoli1 = (Sheep) sheep.deepClone();
        Sheep duoli2 = (Sheep) sheep.deepClone();
        Sheep duoli3 = (Sheep) sheep.deepClone();
        System.out.println("duoli1:"+duoli1+"hashcode:"+duoli1.hashCode()+"master:"+duoli1.getPerson().hashCode());
        System.out.println("duoli2:"+duoli2+"hashcode:"+duoli2.hashCode()+"master:"+duoli2.getPerson().hashCode());
        System.out.println("duoli3:"+duoli3+"hashcode:"+duoli3.hashCode()+"master:"+duoli3.getPerson().hashCode());
    }
}

4.原型模式的优缺点
- 优点: 
  - 如果创建对象的过程十分复杂,使用原型模式可以提高效率
- 原始对象发生变化,克隆对象也会变化,不用手动修改代码
- 可以动态地获取对象的状态
 
- 缺点 
  - 深拷贝代码比较复杂
- 需要对每一个类准备一个克隆方法,如果是创建的新类,问题不大,但是如果是要修改已有的类,就涉及到修改源代码,违背了ocp原则(需求变化时,尽量通过扩展实体而不是修改源代码来实现)
 
5.原型模式在java中的应用
spring中的:@Scope(“prototype”)注解,默认值是singleton,也就是单例,但是当值为prototype就意味着对象变成了多例,为每一个线程都提供了一个对象。



















