一个故事一个模式-原型模式

图片

积千里跬步,汇万里江河;每天进步一点点,终有一天将成大佬

所有源代码都在这:https://github.com/z573419235/GofDemo

各位大佬记得点个星星哦

前言

    前几天生病了,每天头昏脑胀的,诶,生病的时候才知道身体健康的重要性,以后还是要加强锻炼,身体是革命的本钱;

    隔了差不多有五六天没写日志了,罪过罪过;好了,今天要说的是原型模式,原型模式在`Java`中核心秘密就是`clone`这个方法,通过重新`Object`中的`clone`方法.来达到原型模式;而要重新`clone`方法就必须要实现`Cloneable`这个接口,不实现这个接口的话就会报`java.lang.CloneNotSupportedException`异常;

我是鸣人

    鸣人最喜欢的就是吃拉面,就算是上课的时候也是心心念念的想着一乐大叔的拉面

图片

先来看看鸣人的原型实体类:

/**
 * @author zheng
 *
 * 我是鸣人实体类
 */
@Data
public class Naruto implements Cloneable{
    /**
     * 姓名
     * */
    private String name="鸣人";
    /**
     * 年龄
     * */
    private int age=13;
    /**
     * 任务
     * */
    private String task;
    /**
     *爱好
     * */
    private ArrayList<String> hobby=new ArrayList<>();
    /**
     * 构造方法
     * */
    public Naruto(){
        this.hobby.add("吃拉面");
        this.hobby.add("泡温泉");
    }

    /**
     * 重写Object类的clone方法
     * */
    @Override
    public Naruto clone(){
        Naruto naruto=null;
        try {
            naruto=(Naruto)super.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
        return naruto;
    }

    @Override
    public String toString() {
        return "Naruto{" +
                "name='" + name + '\'' +
                ", age='" + age + '\'' +
                ", task='" + task + '\'' +
                ", hobby=" + hobby +
                '}';
    }
}

为了代码整洁,我安装了lombok插件,所以不用写get/set方法,直接加个@Data注解就可以了;

一天,鸣人上着伊鲁卡老师的课,可是心里还是念念不忘一乐大叔的拉面,想着前几天刚学了影分身之术,想着用分身术逃出去吃拉面.于是他就有变了一个分身留着这上课,自己却跑去吃拉面了;

/**
 * 原型模式
 * @author zheng
 * */
public class Main {
    public static void main(String[] args) {
        //我是鸣人本人
        Naruto naruto=new Naruto();
        //我是影分身
        Naruto narutoYin=naruto.clone();

        narutoYin.setTask("上课");
        naruto.setTask("吃拉面");

        System.out.println("鸣人本人:"+naruto.toString());
        System.out.println("影分身:"+narutoYin.toString());

    }
}

//控制台输出
鸣人本人:Naruto{name='鸣人', age='13', task='吃拉面', hobby=[吃拉面, 泡温泉]}
影分身:Naruto{name='鸣人', age='13', task='上课', hobby=[吃拉面, 泡温泉]}

可以看到,鸣人本人的任务是去吃拉面,他的影分身的任务是留着教室上课;当然鸣人可以通过他本人创建无数个影分身,同时执行多个任务;这就是原型模式;

图片

浅拷贝和深拷贝

原型模式就是通过一个原型clone出多个和原型一样的类,但是拷贝也分浅拷贝和深拷贝;

浅拷贝

浅拷贝有多浅,浅到就相当于没有给你拷贝,他就是让你和原型共用一个空间,没有给你分配新的内存;

比如上面的鸣人本人有爱好,但是隐分身一般是没有爱好的,所以创建隐分身要吧爱好给清除调:

/**
 * 原型模式
 * @author zheng
 * */
public class Main {
    public static void main(String[] args) {
        //我是鸣人本人
        Naruto naruto=new Naruto();
        //我是影分身
        Naruto narutoYin=naruto.clone();

        narutoYin.setTask("上课");
        //影分身不配有爱好
        narutoYin.getHobby().clear();
        naruto.setTask("吃拉面");

        System.out.println("鸣人本人:"+naruto.toString());
        System.out.println("影分身:"+narutoYin.toString());

    }
}
//控制台输出
鸣人本人:Naruto{name='鸣人', age='13', task='吃拉面', hobby=[]}
影分身:Naruto{name='鸣人', age='13', task='上课', hobby=[]}

WTF,竟然把本人的爱好也清除调了,那还去吃啥拉面啊,算了算了,安安心心上课吧,诶;叫你上影分身课是时候不认真,失败了吧!!!

深拷贝

深拷贝就是在clone方法里除了克隆类之外,还要克隆引用对象,这样才会重新给引用对象分配新的内存空间

进过上次的教训,鸣人苦练影分身之术,终于学得核心所在,看看他新的影分身技能吧:

图片

在变一个看看:

/**
 * 原型模式
 * @author zheng
 * */
public class Main {
    public static void main(String[] args) {
        //我是鸣人本人
        Naruto naruto=new Naruto();
        //我是影分身
        Naruto narutoYin=naruto.clone();

        narutoYin.setTask("上课");
        //影分身不配有爱好
        narutoYin.getHobby().clear();
        naruto.setTask("吃拉面");

        System.out.println("鸣人本人:"+naruto.toString());
        System.out.println("影分身:"+narutoYin.toString());

    }
}
//控制台输出
鸣人本人:Naruto{name='鸣人', age='13', task='吃拉面', hobby=[吃拉面, 泡温泉]}
影分身:Naruto{name='鸣人', age='13', task='上课', hobby=[]}

哈哈,成功了,这下可以安安心心的区吃拉面了吧;

图片

总结

    引用设计模式之禅的一句话:内部的数组和引用对象才不拷贝,其他的原始类型比如`int`、`long`、`char`等都会被拷贝,但是对于`String`类型,`Java`就希望你把它认为是基本类型,它是没有clone方法的,处理机制也比较特殊,通过字符串池(stringpool)在需要的时候才在内存中创建新的字符串,在使用的时候就把`String`当做基本类使用即可。注意:<font color=orange>使用clone方法,在类的成员变量上就不要增加final关键字,否则当你重新设置这个成员变量的值时是不能设置的,因为final的不可变的,只能引用原来的值</font>