原型(Prototype)模式是一种对象建立型模式,他采起复制原型对象的方法来建立对象的实例。使用原型模式建立的实例,具备与原型同样的数据java
原型模式的特色:设计模式
先写一个支持克隆的类安全
//若是要克隆就必须实现Cloneable接口 public class Person implements Cloneable{ //可能会抛出不支持克隆异常,缘由是没有实现Cloneable接口 @Override protected Person clone(){ try{ return (Person) super.clone(); }catch(CloneNotSupportedException e){ e.printStackTrace(); return null; } } }
这个样子,就说明这个类能够克隆了。ide
这样克隆函数
public class MainClass { public static void main(String[] args) { Person person1 = new Person(); Person person2 = person1.clone(); } }
这样子克隆并不等同于Person p2 = p1;像Person p2 = p1;指的是在栈中建立一个变量p2,将p1的内存地址赋给p2,其实指的是同一个对象。而克隆是复制出一份如出一辙的对象,两个对象内存地址不一样,但对象中的结构与属性值如出一辙。this
这种不经过 new 关键字来产生一个对象,而是经过对象拷贝来实现的模式就叫作原型模式,这个模式的核心是一个clone( )方法,经过这个方法进行对象的拷贝,Java 提供了一个 Cloneable 接口来标示这个对象是可拷贝的,为何说是“标示”呢?翻开 JDK 的帮助看看 Cloneable 是一个方法都没有的,spa
这个接口只是一个标记做用,在 JVM 中具备这个标记的对象才有可能被拷贝,因此覆盖了覆盖clone()方法就能够了。.net
在 clone()方法上增长了一个注解@Override, 没有继承一个类为何能够重写呢?在 Java 中全部类的父类是Object 类,每一个类默认都是继承了这个类,因此这个用上@Override是很是正确的。原型模式虽然很简单,可是在 Java 中使用原型模式也就是 clone 方法仍是有一些注意事项的:设计
对象拷贝时,类的构造函数是不会被执行的。 一个实现了 Cloneable 并重写了 clone 方法的类 A,有一个无参构造或有参构造 B,经过 new 关键字产生了一个对象 S,再而后经过 S.clone()方式产生了一个新的对象 T,那么在对象拷贝时构造函数 B 是不会被执行的, 对象拷贝时确实构造函数没有被执行,这个从原理来说也是能够讲得通的,Object 类的 clone 方法的 原理是从内存中(具体的说就是堆内存)以二进制流的方式进行拷贝,从新分配一个内存块,那构造函数 没有被执行也是很是正常的了。code
还有就是深度克隆与浅度克隆
首先,是浅度克隆
//若是要克隆就必须实现Cloneable接口 public class Person implements Cloneable{ private String name; private String sex; private List<String> list; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public List<String> getList() { return list; } public void setList(List<String> list) { this.list = list; } //可能会抛出不支持克隆异常,缘由是没有实现Cloneable接口 @Override protected Person clone(){ try{ return (Person) super.clone(); }catch(CloneNotSupportedException e){ e.printStackTrace(); return null; } } }
这就是浅度克隆,当被克隆的类中有引用对象(String或Integer等包装类型除外)时,克隆出来的类中的引用变量存储的仍是以前的内存地址,也就是说克隆与被克隆的对象是同一个。这样的话两个对象共享了一个私有变量,全部人均可以改,是一个种很是不安全的方式,在实际项目中使用仍是比较少的。
因此就要说到深度拷贝
//若是要克隆就必须实现Cloneable接口 public class Person implements Cloneable{ private String name; private String sex; private List<String> list; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public List<String> getList() { return list; } public void setList(List<String> list) { this.list = list; } //可能会抛出不支持克隆异常,缘由是没有实现Cloneable接口 @Override protected Person clone(){ try{ Person person = (Person) super.clone(); List<String> newList = new ArrayList(); for(String str : this.list){ newList.add(str); } person.setList(newList); return person; }catch(CloneNotSupportedException e){ e.printStackTrace(); return null; } } }
这样就完成了深度拷贝,两种对象互为独立,属于单独对象。
注意:final 类型修饰的成员变量不能进行深度拷贝
一、在建立对象的时候,咱们不仅是但愿被建立的对象继承其基类的基本结构,还但愿继承原型对象的数据。
二、但愿对目标对象的修改不影响既有的原型对象(深度克隆的时候能够彻底互不影响)。
三、隐藏克隆操做的细节,不少时候,对对象自己的克隆须要涉及到类自己的数据细节。
四、类初始化须要消化很是多的资源,这个资源包括数据、硬件资源等;
五、经过 new 产生一个对象须要很是繁琐的数据准备或访问权限,则可使用原型模式;
六、一个对象须要提供给其余对象访问,并且各个调用者可能都须要修改其值时,能够考虑使用原型模式拷贝多个对象供调用者使用。在实际项目中,原型模式不多单独出现,通常是和工厂方法模式一块儿出现,经过 clone的方法建立一个对象,而后由工厂方法提供给调用者。原型模式先产生出一个包含
大量共有信息的类,而后能够拷贝出副本,修正细节信息,创建了一个完整的个性对象。
原型模式能够说是全部设计模式中最简单的一个,它没有复杂的继承体系,只须要使须要具备拷贝功能的类实现Cloneable接口并重写clone()方法便可。但它的应用却及其普遍,它将对一个对象中各个字段(不论是私有的仍是共有的)的复制操做封装在了clone()方法中,这样,使用该类的用户就不须要对对象中的各个字段的细节进行了解,直接调用clone()方法就能够实现对象的拷贝,并且,经过clone()方法还能够为不一样的字段设置被复制的权限,从而容许仅对能够被复制的字段进行复制。