一、基本介紹
原型模式(建立型):用一個已經建立好的對象作為原型,通過複制該原型對象建立出相同或相似的對象。
二、包含角色
1.抽象原型類:規定了具體原型對象必須實作的接口。
2.具體原型類:實作抽象原型類的 clone() 方法,它是可被複制的對象。
三、案例及UML類圖
案例說明:
當做試卷的時候,我們需要抄别人的試卷,則需要複制對原來的試卷進行複制操作。
UML類圖:
方式一:淺克隆
類TestPaper1:
public class TestPaper1 implements Cloneable{
private String name;
private int clazz;
private String content;
private Integer rank;
private Date date;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getClazz() {
return clazz;
}
public void setClazz(int clazz) {
this.clazz = clazz;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Integer getRank() {
return rank;
}
public void setRank(Integer rank) {
this.rank = rank;
}
@Override
public TestPaper1 clone() throws CloneNotSupportedException {
return (TestPaper1) super.clone();
}
}
說明:試卷類,具體原型類,需要實作抽象原型接口Cloneable。
類Cloneable:
public interface Cloneable {
}
說明:克隆接口,抽象原型類,該類并無clone方法,因為在java中clone是Object的,是以就不必再有clone方法,但使用clone必須實作該接口。
類PrototypeTest1:
public class PrototypeTest1 {
public static void main(String[] args) throws CloneNotSupportedException, InterruptedException {
TestPaper1 testPaper = new TestPaper1();
testPaper.setClazz(654);
testPaper.setName("xuye");
testPaper.setContent("題目内容");
testPaper.setDate(new Date());
testPaper.setRank(1);
TestPaper1 testPaper1 = testPaper.clone();
System.out.println(testPaper.getDate() == testPaper1.getDate());
}
}
說明:測試類及用戶端,淺克隆輸出的結果為true。
方式二:深克隆
類TestPaper2:
public class TestPaper2 implements Cloneable, Serializable {
private String name;
private int clazz;
private String content;
private Integer rank;
private Date date;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getClazz() {
return clazz;
}
public void setClazz(int clazz) {
this.clazz = clazz;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public Integer getRank() {
return rank;
}
public void setRank(Integer rank) {
this.rank = rank;
}
@Override
public TestPaper2 clone() {
TestPaper2 testPaper2 = null;
ByteArrayOutputStream byteArrayOutputStream = null;
ObjectOutputStream objectOutputStream = null;
ByteArrayInputStream byteArrayInputStream = null;
ObjectInputStream objectInputStream = null;
try {
byteArrayOutputStream = new ByteArrayOutputStream();
objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
byte[] bytes = byteArrayOutputStream.toByteArray();
byteArrayInputStream = new ByteArrayInputStream(bytes);
objectInputStream = new ObjectInputStream(byteArrayInputStream);
testPaper2 = (TestPaper2) objectInputStream.readObject();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}finally {
try {
objectInputStream.close();
byteArrayInputStream.close();
objectOutputStream.close();
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return testPaper2;
}
}
說明:試卷類,具體原型類,需要實作抽象原型接口Cloneable。
類Cloneable:
public interface Cloneable {
}
說明:克隆接口,抽象原型類,該類并無clone方法,因為在java中clone是Object的,是以就不必再有clone方法,但使用clone必須實作該接口。
類PrototypeTest2:
public class PrototypeTest2 {
public static void main(String[] args) throws CloneNotSupportedException, InterruptedException {
TestPaper2 testPaper = new TestPaper2();
testPaper.setClazz(654);
testPaper.setName("xuye");
testPaper.setContent("題目内容");
testPaper.setDate(new Date());
testPaper.setRank(1);
TestPaper2 testPaper1 = testPaper.clone();
System.out.println(testPaper.getDate() == testPaper1.getDate());
}
}
說明:測試類及用戶端,淺克隆輸出的結果為false。
四、适用場景
1.适用于對模闆進行重複建立的場景,如抄作業,抄試卷,列印機列印等,深克隆模式用在需要對引用類型的成員變量進行修改時使用。
五、其它
深克隆和淺克隆差別:
淺克隆:對于基礎資料類型的成員變量,直接指派,對于引用類型的成員變量則是把原型中該成員變量的位址拷貝至克隆對象的成員變量中,即引用類型的成員變量位址和原型成員變量一緻。
深克隆:對于基礎資料類型的成員變量,直接指派,對于引用類型的成員變量是在堆中開辟一個空間,把原型對象中該成員變量的值拷貝過去,其位址已經發生改變。即引用類型的成員變量位址和原型成員變量不一緻,但值相同。
深克隆的方式:
在java中深克隆有多種方式,本文使用的是序列化,該方式要求牽扯到原型對象的類都需要實作序列化接口,即Serializable接口,當然還可以使用例如json進行深克隆,不需要實作任何接口,但效率不如序列化。
注意事項:
在java中clone方法是Objcet類的方法,所有的類都預設是繼承了Objcet類,是以Cloneable接口是空方法,是一個标記接口,如果需要淺克隆,直接調用Objcet的clone方法實作Cloneable接口即可,虛拟機會進行其對應的操作。