使用原型实例指定待建立对象的种类,并经过拷贝该原型来建立新的对象。Prototype模式容许一个原型对象克隆(复制)出多个与其相同的对象,而无需知道任何如何建立的细节。java
在实际应用中,Prototype模式不多单独出现。常常与其余模式混用。数组
全部Java类都继承自java.lang.Object
,而Object类提供clone()方法来克隆对象。所以,Java类实现Cloneable接口并重写clone()方法后,便可实现Prototype模式。框架
实现Prototype模式的示例代码以下:ide
// 原型类(也可定义为interface Prototype extends Cloneable) abstract class Prototype implements Cloneable { private String name; public void setName(String name) {this.name = name;} public String getName() {return this.name;} public abstract void doSomething(); @Override public Object clone() { // clone()方法用public修饰 Object object = null; try { object = super.clone(); // 调用父类clone()方法 } catch (CloneNotSupportedException e) { e.printStackTrace(); } return object; } } // 具体实现类(继承自Prototype类自动具备克隆功能) class ConcretePrototype extends Prototype{ public ConcretePrototype() { setName("ConcretePrototype"); } @Override public void doSomething() { System.out.println("I'll clone myself!"); } } public class PrototypeDemo { public static void main(String[] args) { ConcretePrototype cp = new ConcretePrototype(); cp.doSomething(); for(int i=0; i< 5; i++){ ConcretePrototype cloned = (ConcretePrototype)cp.clone(); System.out.println("I'm cloned by " + cloned.getName()); } } }
执行后输出以下:性能
I'll clone myself! I'm cloned by ConcretePrototype I'm cloned by ConcretePrototype I'm cloned by ConcretePrototype I'm cloned by ConcretePrototype I'm cloned by ConcretePrototype
Prototype模式的优势以下:this
缺点以下:prototype
clone()方法只拷贝对象中的基本数据类型,而不会拷贝数组、容器对象、引用对象等。若要实现深拷贝,必须将Prototype模式中的数组、容器对象、引用对象等另行拷贝。
例如,深拷贝一个对象时,该对象必须实现Cloneable接口并重写clone()方法,并在clone()方法内部将该对象引用的其余对象也克隆一份。同理,被引用的对象也要作一样的处理。
注意,Boolean、Byte、Character、Class、Double、Float、Integer、Long、Short、String以及大部分的Exception的子类均为不可变类,其对象没有必要实现深拷贝。此外,大部分的Java容器类都已实现克隆功能。code
相较而言,clone()方法更适合数组深拷贝。但须要注意如下两点:对象
(1) 基本类型的一维数组可经过诸如(int[])data.clone()
的形式克隆,而基本类型的"二维数组"(其实是类型诸如int[]的一维数组)须要按维克隆。例如:继承
public static int[][] twoDimensionArrayClone (int[][] tdArray){ int[][] copy = tdArray.clone(); for (int i = 0; i < tdArray.length; i++) { copy[i] = tdArray[i].clone(); } return copy; }
(2) 当数组元素为普通Java对象时,克隆数组后还要克隆数组中包含的对象。如下示例详细展现了数组对象的不一样浅拷贝和深拷贝方式:
class Person implements Cloneable { public String name; public Person(String name) {this.name = name;} @Override public Object clone() { Object object = null; try { object = super.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } return object; } } public class ArrayPrototype { public static void main(String[] args) { Person[] origin = new Person[] { new Person("Mike"), new Person("Jack"), new Person("Jason") }; Person[] copyOf = Arrays.copyOf(origin, origin.length); // 浅拷贝(内部调用System.arraycopy,返回新数组) Person[] arrayCopy = new Person[origin.length]; // 浅拷贝(可拷贝部分数组) System.arraycopy(origin, 0, arrayCopy, 0, origin.length); Person[] clonedCopy = origin.clone(); // 浅拷贝 System.out.println("origin=copyOf=arrayCopy=clonedCopy=" + (origin[0] == copyOf[0] && copyOf[1] == arrayCopy[1] && arrayCopy[2] == clonedCopy[2])); clonedCopy[0].name = "Lily"; System.out.println("Shallow Person[0]: " + origin[0].name + " -> " + clonedCopy[0].name); Person[] deepCopy = new Person[origin.length]; // 深拷贝 for(int i = 0; i < origin.length; i++) { deepCopy[i] = (Person) origin[i].clone(); } deepCopy[1].name = "Lily"; System.out.println("Deep Person[1]: " + origin[1].name + " -> " + clonedCopy[1].name); Person[] deepCopy2 = Arrays.stream(origin).map(Person::clone).toArray(Person[]::new); // 深拷贝 deepCopy2[2].name = "Lucy"; System.out.println("Deep Person[2]: " + origin[2].name + " -> " + deepCopy2[2].name); //origin=copyOf=arrayCopy=clonedCopy=true //Shallow Person[0]: Lily -> Lily //Deep Person[1]: Jack -> Jack //Deep Person[2]: Jason -> Lucy } }