设计模式之原型模式

前言

在现实世界中,咱们一般会感受到分身乏术。要是本身有分身那该多好啊,一个用来工做,一个用来看电视,一个用来玩游戏(无心中透露了本身单身狗的身份-。-),其实就是克隆,这种技术存在着很大的弊端,因此如今是禁止使用的。可是这种复制技术在java的世界里早已出现,就是原型模式java

什么是原型模式

用原型实例指定建立对象的种类,而且经过拷贝这些原型建立新的对象数据库

uml类图

clipboard.png

原型模式是设计模式中最简单的,没有之一。由于它的核心就是一个clone方法,经过这个方法完成对象的克隆。java提供了cloneable接口来标示这个对象是有可能被克隆的,这个接口只具备标记做用,在jvm中只有具备这个标记的对象才有可能被克隆。有可能克隆变成能够被克隆,就须要咱们重写clone方法设计模式

浅拷贝

public class Person implements Cloneable{

    public Person() {
        System.out.println("构造函数执行了");
    }

    private String name;

    public String getName() {
        return name;
    }

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

    @Override
    protected Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }
}
public class Client {

    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        person.setName("bin");

        Person person1=person.clone();
        person1.setName("123");
        System.out.println(person.getName()+"---"+person1.getName());
    }
}
测试结果:
构造函数执行了
bin---123

经过以上例子能够得知,实现cloneable接口,复写clone方法,便可对对象进行复制。有一个问题clone方法是怎么建立对象的?能够看到,构造函数只执行了一次,这就说明clone方法是不会调用构造函数的,它实际上是内存二进制流的拷贝,比直接new对象性能要好不少。
标题是浅拷贝,那到底什么是浅拷贝呢?先不要着急,咱们来改动下例子:数组

public class Person implements Cloneable{

    private List<String> valueList = new ArrayList<>();

    public void setValue(){
        valueList.add("1");
    }
    public List<String> getValue(){
        return valueList;
    }
    
    @Override
    protected Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }
}
public class Client {

    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        person.setValue();

        Person person1=person.clone();
        person1.setValue();
        System.out.println(person1.getValue());
    }
}
测试过程:
构造函数执行了
[1, 1]

或许你会很费解明明进行了拷贝,应该只会打印一个“1”啊,,为何person1.getValue()会打印出两个“1”呢?由于java作了一个偷懒的拷贝动做,Object提供的clone方法只拷贝本对象,对其引用对象和内部数组都不拷贝,只是将地址拷贝过来用,这种拷贝方式就是浅拷贝。可是String对象例外,由于java本就但愿将String当作基本数据类型,它没有clone方法,而且它的处理机制很是特殊,经过字符串池在内存中建立新的字符串,咱们只要把其看待成基本数据类型就能够了。安全

深拷贝

当拷贝的对象中有引用对象或者数组时,咱们经过浅拷贝得到复制对象是不安全的。怎么解决呢?咱们能够经过深拷贝来解决这个问题,下面继续改造例子学习深拷贝:jvm

public class Person implements Cloneable{

    private ArrayList<String> valueList = new ArrayList<>();

    public void setValue(){
        valueList.add("1");
    }
    public List<String> getValue(){
        return valueList;
    }

    @Override
    protected Person clone() throws CloneNotSupportedException {
        Person person=(Person) super.clone();
        person.valueList= (ArrayList<String>) this.valueList.clone();
        return person;
    }
}
public class Client {

    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        person.setValue();

        Person person1=person.clone();
        person1.setValue();
        System.out.println(person.getValue());
        System.out.println(person1.getValue());
    }
}
测试结果:
person:[1]
person1[1, 1]

经过对引用对象和数组的进行独立的拷贝,就完成了深拷贝,每一个person对象都会有本身的valueList。ide

clone和final

对象的clone和对象内的final是互相冲突的,下面咱们来看个图片:函数

clipboard.png
当咱们将valueList增长final关键字,对其clone编译会报错,解决办法就是不要加final关键字(感受像是废话...)性能

总结

原型模式和现实世界中说的克隆基本同样。原型模式是内存二进制流的拷贝,比new对象性能高不少,尤为是当建立这个对象须要数据库或者其余硬件资源时尤其明显。另外咱们须要注意的就是当复制的对象存在引用对象和数组时,要根据实际业务选择深拷贝仍是浅拷贝。学习

相关文章
相关标签/搜索