原型模式

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

剖析原理

克隆分类 实现
浅克隆 只复制基本类型的数据,引用类型的数据只复制了引用的地址,引用的对象并没有复制,在新的对象中修改引用类型的数据会影响原对象中的引用。
深克隆 方式一:嵌套重写clone方法:实现Cloneable接口(引用数据类型也要实现Cloneable接口),重写clone方法,clone的嵌套,复制后的对象与原对象之间完全不会影响。
方式二:序列化对象:实现序列化Serializable接口(不实现Cloneable接口),(引用数据类型也要实现Serializable),对象序列化后写入流中,此时不存在引用数据类型的概念,从流中读取,生成新的对象,新对象和原对象之间也是完全互不影响的。

分析实现步骤

类图
image.png

浅克隆

  1. 实现Cloneable接口,重写clone方法
  2. 客户端测试
  3. 分析

    深克隆

  4. 深克隆

  • 方式一:实现Cloneable,嵌套重写clone方法
  • 方式二:实现序列化接口,以流的形式实现拷贝(推荐)
  1. 客户端测试
  2. 分析

    代码实现

    浅克隆

  3. 实现Cloneable接口,重写clone方法 ```java /**

    • 浅克隆 - 克隆羊 */ public class Sheep implements Cloneable{

      private String name; private String color; private Sheep brother;

      //———————————————————↓

      //浅克隆 @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }

      //———————————————————↑

      public String getName() { return name; }

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

      public String getColor() { return color; }

      public void setColor(String color) { this.color = color; }

      public Sheep getBrother() { return brother; }

      public void setBrother(Sheep brother) { this.brother = brother; }

      @Override public String toString() { return “Sheep{“ +

      1. "name='" + name + '\'' +
      2. ", color='" + color + '\'' +
      3. '}';

      } }


2. 客户端测试
```java
/**
 * 浅拷贝测试
 */
public class ShallowTest {

    public static void main(String[] args) {
        Sheep sheep1 = new Sheep();
        sheep1.setName("多莉");
        sheep1.setColor("白色");

        Sheep sheepBrother = new Sheep();
        sheepBrother.setName("多莉兄弟");
        sheepBrother.setColor("花色");
        sheep1.setBrother(sheepBrother);
        System.out.println("多莉:"+sheep1+",brother:"+sheep1.getBrother().hashCode());

        try {
            Sheep sheep2 = (Sheep)sheep1.clone();
            System.out.println("克隆羊1:"+sheep2+",brother:"+sheep2.getBrother().hashCode());
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
}

//测试结果
多莉:Sheep{name='多莉', color='白色'},brother:668849042
克隆羊1:Sheep{name='多莉', color='白色'},brother:668849042
  1. 分析

只复制基本类型的数据,引用类型的数据(brother)只复制了引用的地址,而不是新建一个对象

深克隆

方式一:嵌套重写clone方法

  1. 嵌套重写clone方法 ```java /**

    • 深克隆 - 方式一:嵌套重写clone方法 */ public class DeepSheep implements Cloneable{ private String name; private String color; private Sheep brother;

      //———————————————————↓ /**

      • 方式一:重写clone方法,实现深拷贝
      • 1.先浅拷贝一次
      • 2.对象clone并强转一次 *
      • 注:该方式的缺陷是需要单独处理所有要克隆的类中的引用数据类型(Sheep) */ @Override protected Object clone(){ DeepSheep deepSheep = null; try {
         //先浅拷贝一次
         deepSheep = (DeepSheep) super.clone();
         deepSheep.brother = (Sheep)this.brother.clone();
        
        } catch (CloneNotSupportedException e) {
         e.printStackTrace();
        
        } return deepSheep; }

      //———————————————————↑

public String getName() {
    return name;
}

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

public String getColor() {
    return color;
}

public void setColor(String color) {
    this.color = color;
}

public Sheep getBrother() {
    return brother;
}

public void setBrother(Sheep brother) {
    this.brother = brother;
}

@Override
public String toString() {
    return "DeepSheep{" +
            "name='" + name + '\'' +
            ", color='" + color + '\'' +
            '}';
}

}


2. 客户端测试
```java
public class DeepTest {

    public static void main(String[] args) {
        DeepSheep sheep1 = new DeepSheep();
        sheep1.setName("多莉");
        sheep1.setColor("白色");

        Sheep sheepBrother = new Sheep();
        sheepBrother.setName("多莉兄弟");
        sheepBrother.setColor("花色");
        sheep1.setBrother(sheepBrother);
        System.out.println("多莉:"+sheep1+",brother:"+sheep1.getBrother().hashCode());

        DeepSheep sheep2 = (DeepSheep)sheep1.clone();
        System.out.println("克隆羊1:"+sheep2+",brother:"+sheep2.getBrother().hashCode());
    }
}

//测试结果
多莉:DeepSheep{name='多莉', color='白色'},brother:434176574
克隆羊1:DeepSheep{name='多莉', color='白色'},brother:2096057945
  1. 分析

可看出引用类型是不同的,即重新复制了一份引用对象。方式一的缺陷是需要单独处理所有要克隆的类中的引用数据类型,即每个引用类型都需要重写一个clone()方法,比较麻烦。

方式二:序列化对象(推荐)

  1. 实现序列化接口,以流的形式实现拷贝(推荐) ```java /**

    • 深克隆 - 方式二:序列化对象 */ public class DeepSheep2 implements Serializable {

      private static final long serialVersionUID = 1L;

      private String name; private String color; //注意该对象需要Serializable,否则在转流的时候不成功 private Sheep2 brother;

      //———————————————————↓ public DeepSheep2 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);
      
         //将流对象反序列化,从而实现类的深拷贝
         return (DeepSheep2) ois.readObject();
      

      } catch (IOException | ClassNotFoundException e) {

         e.printStackTrace();
         return null;
      

      } finally {

         try {
             //资源释放
             bos.close();
             bis.close();
             oos.close();
             ois.close();
         } catch (IOException e) {
             e.printStackTrace();
         }
      

      } } //———————————————————↑

public String getName() {
    return name;
}

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

public String getColor() {
    return color;
}

public void setColor(String color) {
    this.color = color;
}

public Sheep2 getBrother() {
    return brother;
}

public void setBrother(Sheep2 brother) {
    this.brother = brother;
}

@Override
public String toString() {
    return "DeepSheep2{" +
            "name='" + name + '\'' +
            ", color='" + color + '\'' +
            '}';
}

}

```java
/**
 * 深克隆 - 克隆羊 
 * 注意:此处的对象是要 implements Serializable
 */
public class Sheep2 implements Serializable {

    private String name;
    private String color;
    private Sheep2 brother;

    public String getName() {
        return name;
    }

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

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    public Sheep2 getBrother() {
        return brother;
    }

    public void setBrother(Sheep2 brother) {
        this.brother = brother;
    }

    @Override
    public String toString() {
        return "Sheep{" +
                "name='" + name + '\'' +
                ", color='" + color + '\'' +
                '}';
    }
}
  1. 客户端测试 ```java /**

    • 深拷贝测试 */ public class DeepTest2 {

      public static void main(String[] args) { DeepSheep2 sheep1 = new DeepSheep2(); sheep1.setName(“多莉”); sheep1.setColor(“白色”);

      Sheep2 sheepBrother = new Sheep2(); sheepBrother.setName(“多莉兄弟”); sheepBrother.setColor(“花色”); sheep1.setBrother(sheepBrother); System.out.println(“多莉:”+sheep1+”,brother:”+sheep1.getBrother().hashCode());

      DeepSheep2 sheep2 = sheep1.deepClone(); System.out.println(“克隆羊1:”+sheep2+”,brother:”+sheep2.getBrother().hashCode()); } }

//测试结果 多莉:DeepSheep2{name=’多莉’, color=’白色’},brother:434176574 克隆羊1:DeepSheep2{name=’多莉’, color=’白色’},brother:1874154700 ```

  1. 分析

一次性序列化以流的方式,一劳永逸,唯一要注意点是,引用对象是要implements Serializable否则会转流失败

框架或项目源码分析

Spring以xml方式创建bean时,选择scope="prototype"时,采用的是原型模式进行创建bean

  1. 自定义的xml配置文件

image.png

  1. 编写测试类

image.png

  1. 跟踪源码

image.png

image.png

可知进入了原型模式的方法中


应用场景

  1. 如果类的初始化需要耗费较多的资源,那么可以通过原型拷贝避免这些消耗。
  2. 通过new产生一个对象需要非常繁琐的数据准备或访问权限,则可以使用原型模式。
  3. 一个对象需要提供给其他对象访问,而且各个调用者可能都需要修改其值时,可以拷贝多个对象供调用者使用,即保护性拷贝。