Java设计模式——原型模式

Java设计模式之原型模式

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

较为简单,经常与其他模式一同使用

  • 实现Cloneable接口,重写clone方法

原型模式设计到深拷贝和浅拷贝

深拷贝与浅拷贝

  • 深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,也就是使用clone方法后创建一个新的内存空间存储引用。
  • 浅拷贝:对基本数据类型进行值传递,对引用数据类型进行引用传递般的拷贝,也就是使用clone方法后基本类型值相同,引用类型引用地址相同,指向同一个原型。

深拷贝与浅拷贝问题中,会发生深拷贝的有java中的8中基本类型以及他们的封装类型,另外还有String类型。其余的都是浅拷贝。

使用

场景:绵羊有颜色、名字和年龄三个成员变量,其中颜色是具体的类对象,名字和年龄是String,我们知道String会发生深拷贝,但是颜色对象是浅拷贝,我们现在尝试使用原型模式创建两只克隆羊

浅拷贝
  • Color 颜色
/**
 * 颜色
 */
public class Color implements Cloneable {
    private String color;

    public Color(String color) {
        this.color = color;
    }

    public String getColor() {
        return color;
    }
    //重写clone方法
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Color color = null;
        color = (Color) super.clone();
        return color;
    }
}
  • Sheep 绵羊类

使用了lombok插件提供getter/setter构造方法,不会自行百度

@Data
@AllArgsConstructor
public class Sheep implements Cloneable{
    private Color color;//保存引用 非基本类型
    private String name;
    private String age;
    @Override
    public String toString() {
        return "Sheep{" +
                "color=" + color.getColor() +
                ", name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Sheep sheep = null;
        sheep = (Sheep) super.clone();
        return sheep;
    }
}
  • 测试
public class ClientTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        Color color = new Color("white");
        Sheep sheep = new Sheep(color,"小白","12");
        Sheep cloneSheep1 = (Sheep) sheep.clone();
        Sheep cloneSheep2 = (Sheep) sheep.clone();
        System.out.println("sheep:"+sheep);
        System.out.println("cloneSheep1:"+cloneSheep1);
        System.out.println("cloneSheep2:"+cloneSheep2);
        System.out.println("\n");
        System.out.println("cloneSheep1是否等于sheep:"+ (cloneSheep1 == sheep));
        System.out.println("cloneSheep1是否等于cloneSheep2:"+ (cloneSheep1 == cloneSheep2));
        System.out.println("\n");
        System.out.println("sheep中非基本类型Color是否与cloneSheep1中Color相等:"+(sheep.getColor()==cloneSheep1.getColor()));
        System.out.println("cloneSheep2中非基本类型Color是否与cloneSheep1中Color相等:"+(cloneSheep2.getColor()==cloneSheep1.getColor()));
    }
}

输出:
sheep:Sheep{color=white, name='小白', age='12'}
cloneSheep1:Sheep{color=white, name='小白', age='12'}
cloneSheep2:Sheep{color=white, name='小白', age='12'}

cloneSheep1是否等于sheep:false
cloneSheep1是否等于cloneSheep2:false

sheep中非基本类型Color是否与cloneSheep1中Color相等:true
cloneSheep2中非基本类型Color是否与cloneSheep1中Color相等:true

说明浅拷贝复制的对象是不同的,但是对象中的引用型成员变量指向同一个引用

深拷贝

深拷贝有两种方法:序列化更改clone方法

  • Color 颜色
@AllArgsConstructor
@Data
public class Color implements Serializable,Cloneable {
    private String color;
    @Override
    protected Object clone() throws CloneNotSupportedException {
        Color color = null;
        color = (Color) super.clone();
        return color;
    }
}
  • DeepCloneSheep
/**
 * 深拷贝绵羊,让它的颜色Color拷贝时创建新的引用
 */
@Data
@AllArgsConstructor
public class DeepCloneSheep implements Serializable,Cloneable {
    private Color color;//保存引用 非基本类型
    private String name;//String类型
    private String age;
    /**
     * 深克隆方法一 修改clone方法
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        DeepCloneSheep sheep = null;
        sheep = (DeepCloneSheep) super.clone();//sheep先克隆
        sheep.color = (Color) color.clone();//其中非基本类型再克隆
        return sheep;
    }
    /**
     * 方法2.使用序列化
     * @return
     */
    public Object deepClone(){
        Object o = null;
        ByteArrayOutputStream bos;
        ObjectOutputStream oos;
        ByteArrayInputStream bis;
        ObjectInputStream ois;
        try{
            //序列化 对象流
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(this);
            //反序列化
            bis = new ByteArrayInputStream(bos.toByteArray());
            ois = new ObjectInputStream(bis);
            o = ois.readObject();
            bos.close();
            bis.close();
            ois.close();
            bis.close();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return o;
    }
    @Override
    public String toString() {//重写toString打印颜色
        return "DeepCloneSheep{" +
                "color=" + color.getColor() +
                ", name='" + name + '\'' +
                ", age='" + age + '\'' +
                '}';
    }
}
  • 测试
public class ClientTest {
    @Test//方法一:修改clone,让颜色也被clone
    public void testCloneMethod1() throws CloneNotSupportedException {
        Color color = new Color("white");
        DeepCloneSheep sheep = new DeepCloneSheep(color,"小白","12");
        DeepCloneSheep cloneSheep1 = (DeepCloneSheep) sheep.clone();
        DeepCloneSheep cloneSheep2 = (DeepCloneSheep) sheep.clone();
        System.out.println("sheep:"+sheep);
        System.out.println("cloneSheep1:"+cloneSheep1);
        System.out.println("cloneSheep2:"+cloneSheep2+"\n");
        System.out.println("方法一:修改clone方法");
        System.out.println("cloneSheep1是否等于sheep:"+ (cloneSheep1 == sheep));
        System.out.println("cloneSheep1是否等于cloneSheep2:"+ (cloneSheep1 == cloneSheep2)+"\n");
        System.out.println("sheep中非基本类型Color是否与cloneSheep1中Color相等:"+(sheep.getColor()==cloneSheep1.getColor()));
        System.out.println("cloneSheep2中非基本类型Color是否与cloneSheep1中Color相等:"+(cloneSheep2.getColor()==cloneSheep1.getColor())+"\n");
    }
//方法二:序列化
    @Test
    public void testCloneMethod2(){
        Color color = new Color("white");
        DeepCloneSheep sheep = new DeepCloneSheep(color,"小白","12");
        DeepCloneSheep cloneSheep3 = (DeepCloneSheep) sheep.deepClone();
        DeepCloneSheep cloneSheep4 = (DeepCloneSheep) sheep.deepClone();
        System.out.println("sheep:"+sheep);
        System.out.println("cloneSheep3:"+cloneSheep3);
        System.out.println("cloneSheep4:"+cloneSheep4+"\n");
        System.out.println("方法二:序列化方法");
        System.out.println("cloneSheep3是否等于sheep:"+ (cloneSheep3 == sheep));
        System.out.println("cloneSheep3是否等于cloneSheep4:"+ (cloneSheep3 == cloneSheep4)+"\n");
        System.out.println("sheep中非基本类型Color是否与cloneSheep3中Color相等:"+(sheep.getColor()==cloneSheep3.getColor()));
        System.out.println("cloneSheep3中非基本类型Color是否与cloneSheep4中Color相等:"+(cloneSheep3.getColor()==cloneSheep4.getColor()));
    }
}

输出:两种方法输出一样

sheep:DeepCloneSheep{color=white, name='小白', age='12'}
cloneSheep1:DeepCloneSheep{color=white, name='小白', age='12'}
cloneSheep2:DeepCloneSheep{color=white, name='小白', age='12'}

cloneSheep1是否等于sheep:false
cloneSheep1是否等于cloneSheep2:false

sheep中非基本类型Color是否与cloneSheep1中Color相等:false
cloneSheep2中非基本类型Color是否与cloneSheep1中Color相等:false

可以看到,深拷贝中引用类型颜色在clone过程中没有再指向同一引用

Spring中原型模式使用

我们都知道在Spring中,创建bean的scope域可以为Singleton或者是protoType,这就是使用到了原型模式

我们创建一个Conponent,使用xml配置声明为原型模式protoType,加入到容器,我们获取两次该Bean看它们是否相等

@Component
public class Bean {
}
<bean id="bean" scope="prototype" name="bean" class="top.dzou.prototype_pattern.spring.Bean"/>

测试:

public class SpringBeanTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        Object bean = context.getBean("bean");//获取该bean
        Object bean2 = context.getBean("bean");//再获取一个
        //打印是否相等,测试原型模式
        //调用BeanFactory中 doGetBean方法 其中判断是否为if (mbd.isPrototype())
        System.out.println("bean是否等于bean2:"+(bean==bean2));
    }
}

输出:bean是否等于bean2:false

源码:

调用getBean时调用了AbstractBeanFactory类中的doGetBean方法,其中有下面部分代码

if (mbd.isSingleton()) {//判断是否为单例模式
                    bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
                } else if (mbd.isPrototype()) {//判断是否为原型模式
                        this.beforePrototypeCreation(beanName);
                        prototypeInstance = this.createBean(beanName, mbd, args);

这就是Spring中原型模式的使用,当然也使用了单例模式

原型模式优点:

  • 某些情况,复制对象比创建对象更有效,clone方法是本地方法,直接操作内存,比创建对象高效不少
  • 简化对象的创建,不被构造方法束缚

代码地址:Github原型模式


Java设计模式|策略模式

Java设计模式|观察者模式

Java设计模式|装饰者模式

Java设计模式|工厂模式

Java设计模式|命令模式

Java设计模式|适配器模式和外观模式

Java设计模式|模板方法模式

Java设计模式|迭代器模式和组合模式

Java设计模式|状态模式

Java设计模式|代理模式

Java设计模式|单例模式

Java设计模式|备忘录模式

Java设计模式|访问者模式

Java设计模式|复合模式

Java设计模式|桥接模式

Java设计模式|生成器模式

Java设计模式|享元模式/蝇量模式

Java设计模式|原型模式

Java设计模式|责任链模式

Java设计模式|中介者模式

  • 本文作者: dzou | 微信:17856530567
  • 本文链接: http://www.dzou.top/post/8fcaf333.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明出处!
  • 并保留本声明和上方二维码。感谢您的阅读和支持!