在现实世界中,咱们一般会感受到分身乏术。要是本身有分身那该多好啊,一个用来工做,一个用来看电视,一个用来玩游戏(无心中透露了本身单身狗的身份-。-),其实就是克隆,这种技术存在着很大的弊端,因此如今是禁止使用的。可是这种复制技术在java的世界里早已出现,就是原型模式java
用原型实例指定建立对象的种类,而且经过拷贝这些原型建立新的对象数据库
原型模式是设计模式中最简单的,没有之一。由于它的核心就是一个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是互相冲突的,下面咱们来看个图片:函数
当咱们将valueList增长final关键字,对其clone编译会报错,解决办法就是不要加final关键字(感受像是废话...)性能
原型模式和现实世界中说的克隆基本同样。原型模式是内存二进制流的拷贝,比new对象性能高不少,尤为是当建立这个对象须要数据库或者其余硬件资源时尤其明显。另外咱们须要注意的就是当复制的对象存在引用对象和数组时,要根据实际业务选择深拷贝仍是浅拷贝。学习