What
是用于創(chuàng)建重復的對象,同時又能保證性能的一種創(chuàng)建型模式。
Why
優(yōu)點:
1、性能提高。當創(chuàng)建新的對象比較復雜時,可以利用原型模式簡化對象的創(chuàng)建過程,同時也能夠提高效率。
因為通過clone創(chuàng)建的一個對象比直接用new創(chuàng)建對象更有效率。
2、逃避構造函數(shù)的約束。
缺點:
- 在實現(xiàn)深克隆的時候可能需要比較復雜的代碼。
- 需要為每一個類配備一個克隆方法,而且這個克隆方法需要對類的功能進行通盤考慮,這對全新的類來說不是很難,但對已有的類進行改造時,不一定是件容易的事,必須修改其源代碼,違背了“開閉原則”。
Where
- 當通過new產(chǎn)生一個對象需要非常繁瑣的數(shù)據(jù)準備或訪問權限。
- 一個對象多個修改者的場景。
- 類初始化需要消化非常多的資源,這個資源包括數(shù)據(jù)、硬件資源等。
- 資源優(yōu)化場景。
How
- 實現(xiàn)Cloneable接口
- 重寫clone方法
示例:一個班級有多個學生,每個學生只對應一個班級。
學生類:
public class Student implements Cloneable {
private String name;
private Integer age;
private Classroom classroom;
public Student() {
System.out.println("Student類的無參構造器...");
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"Name\":\"")
.append(name).append('\"');
sb.append(",\"Age\":")
.append(age);
sb.append(",\"Classroom\":")
.append(classroom);
sb.append('}').append(" ").append(super.toString());
return sb.toString();
}
//省略get、set方法
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
課室類(Classroom):
public class Classroom{
private String className;
public Classroom() {
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"ClassName\":\"")
.append(className).append('\"');
sb.append('}').append(super.toString());
return sb.toString();
}
//省略get、set方法
}
測試類:
public static void main(String[] args) throws CloneNotSupportedException {
Student student1 = new Student();
Classroom classroom = new Classroom();
classroom.setClassName("高三<7>班");
student1.setName("Jone");
student1.setAge(20);
student1.setClassroom(classroom);
Student student2 = (Student) student1.clone();
Student student3 = (Student) student1.clone();
//先觀察clone出來的對象地址
System.out.println("各個對象的地址");
System.out.println(student1.toString());
System.out.println(student2.toString());
System.out.println(student3.toString());
System.out.println("修改student2和student3的屬性");
student2.setName("Marry");
student2.setAge(18);
student3.setName("Stan");
student3.setAge(19);
System.out.println(student1);
System.out.println(student2);
System.out.println(student3);
System.out.println("Marry被調(diào)到高三<1>班");
student2.getClassroom().setClassName("高三<1>班");
System.out.println(student1);
System.out.println(student2);
System.out.println(student3);
}
Student類的無參構造器...
各個對象的地址
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c} Prototype.BlogDemo.Student@74a14482
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c} Prototype.BlogDemo.Student@1540e19d
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c} Prototype.BlogDemo.Student@677327b6
修改student2和student3的屬性
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c} Prototype.BlogDemo.Student@74a14482
{"Name":"Marry","Age":18,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c} Prototype.BlogDemo.Student@1540e19d
{"Name":"Stan","Age":19,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c} Prototype.BlogDemo.Student@677327b6
Marry被調(diào)到高三<1>班
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<1>班"}Prototype.BlogDemo.Classroom@4554617c} Prototype.BlogDemo.Student@74a14482
{"Name":"Marry","Age":18,"Classroom":{"ClassName":"高三<1>班"}Prototype.BlogDemo.Classroom@4554617c} Prototype.BlogDemo.Student@1540e19d
{"Name":"Stan","Age":19,"Classroom":{"ClassName":"高三<1>班"}Prototype.BlogDemo.Classroom@4554617c} Prototype.BlogDemo.Student@677327b6
對于輸出結果,有下面幾個點值得注意:
1.clone對象并不會調(diào)用對象的構造方法。
2.clone出來的對象地址都不一樣,可以說明當執(zhí)行完clone方法的時候,會在堆內(nèi)存中開辟出一塊內(nèi)存給新對象。
3.clone出來的對象屬性都一樣,包括引用對象的內(nèi)存地址都一樣。所以當修改任意對象的引用變量的時候,會把所有含有被修改的引用變量都更改。

Show clone method in heap and stack
關于第三點,其實就是經(jīng)典的一個話題:深克隆和淺克隆。
淺克隆:創(chuàng)建一個新對象,新對象的屬性和原來對象完全相同,對于非基本類型屬性,仍指向原有屬性所指向的對象的內(nèi)存地址。
深克隆:創(chuàng)建一個新對象,屬性中引用的其他對象也會被克隆,不再指向原有對象地址。
很明顯,上面的例子是淺克隆。那深克隆如何做到呢?
首先需要修改Student類和Classroom類
public class Student implements Cloneable {
private String name;
private Integer age;
private Classroom classroom;
public Student() {
System.out.println("Student類的無參構造器...");
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"Name\":\"")
.append(name).append('\"');
sb.append(",\"Age\":")
.append(age);
sb.append(",\"Classroom\":")
.append(classroom);
sb.append('}').append(" ").append(super.toString());
return sb.toString();
}
//省略get、set方法
@Override
protected Object clone() throws CloneNotSupportedException {
Student student = (Student) super.clone();
student.setClassroom((Classroom) student.classroom.clone());
return student;
}
}
public class Classroom implements Cloneable{
private String className;
public Classroom() {
}
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone();
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("{");
sb.append("\"ClassName\":\"")
.append(className).append('\"');
sb.append('}').append(super.toString());
return sb.toString();
}
//省略get、set
}
輸出結果:
Student類的無參構造器...
各個對象的地址
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c} Prototype.BlogDemo.Student@74a14482
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@1540e19d} Prototype.BlogDemo.Student@677327b6
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@14ae5a5} Prototype.BlogDemo.Student@7f31245a
修改student2和student3的屬性
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c} Prototype.BlogDemo.Student@74a14482
{"Name":"Marry","Age":18,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@1540e19d} Prototype.BlogDemo.Student@677327b6
{"Name":"Stan","Age":19,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@14ae5a5} Prototype.BlogDemo.Student@7f31245a
Marry被調(diào)到高三<1>班
{"Name":"Jone","Age":20,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@4554617c} Prototype.BlogDemo.Student@74a14482
{"Name":"Marry","Age":18,"Classroom":{"ClassName":"高三<1>班"}Prototype.BlogDemo.Classroom@1540e19d} Prototype.BlogDemo.Student@677327b6
{"Name":"Stan","Age":19,"Classroom":{"ClassName":"高三<7>班"}Prototype.BlogDemo.Classroom@14ae5a5} Prototype.BlogDemo.Student@7f31245a
由結果可以看出,使用強克隆可以為引用對象開辟一塊內(nèi)存地址而不是像淺克隆那樣指向同一個引用對象。
總結:
設計原型模式需要注意淺克隆和深克隆。