使用原型實例指定待建立對象的種類,並經過拷貝該原型來建立新的對象。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 } }