原型模式
原型模式就是使用複製對象,創建出新的對象,並且不需要知道創建的細節(比如類的屬性的賦值等等)。
基本使用(淺拷貝)
常見的就是使用Object的clone方法。需要讓類實現Cloneable並重寫clone方法,才能使用。
public class A implements Cloneable{
private String code;
private String name;
public A(String code, String name) {
this.code = code;
this.name = name;
}
public void setCode(String code) {
this.code = code;
}
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
public static void main(String[] args) throws CloneNotSupportedException {
A a = new A("00","name1");
A a1 = (A)a.clone();
a1.setCode("01");
A a2 = (A)a.clone();
a2.setCode("02");
System.out.println(a.hashCode());
System.out.println(a1.hashCode());
System.out.println(a2.hashCode());
}
}
結果,很明顯,三個對象是不一樣的。
596512129
824318946
930990596
深拷貝
上面那種方式,是淺拷貝,即只能拷貝A類中的基本數據類型和String。對於A類中如果還有其他引用類型,淺拷貝是實現不了引用類型的拷貝的。
所以,需要進行對A深拷貝B,通常有兩種方法(重寫Clone方法和序列化)。
方法1、重寫A的clone方法,並在方法中對B進行拷貝(當然B類中也要實現淺拷貝)。
public class B implements Serializable,Cloneable{
private String id;
@Override
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
}
public class A implements Serializable,Cloneable{
private String code;
private String name;
private B b;
public A(String code, String name, B b) {
this.code = code;
this.name = name;
this.b = b;
}
public void setCode(String code) {
this.code = code;
}
@Override
public Object clone() throws CloneNotSupportedException {
A a = (A)super.clone();
// 在A的clone方法中,對A的B引用進行clone
a.b = (B)a.b.clone();
return a;
}
public static void main(String[] args) throws CloneNotSupportedException {
A a = new A("00","name1",new B());
A a1 = (A)a.clone();
a1.setCode("01");
A a2 = (A)a.clone();
a2.setCode("02");
System.out.println(a.b.hashCode());
System.out.println(a1.b.hashCode());
System.out.println(a2.b.hashCode());
}
}
執行結果:
596512129
824318946
930990596
看執行結果,每個A實例的B引用,都是不同的hashCode值,說明A中的B也被拷貝了。
如果將A中clone方法中的a.b = (B)a.b.clone();註釋掉,可以得到最終打印出來的三個結果就是一樣的。
方法2、序列化
序列化前提,A和B都要實現Serializable接口。
A和B的定義還是和方法1一樣,我們只重寫A中的clone方法。
@Override
public Object clone() throws CloneNotSupportedException {
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化,以對象的方式輸出去
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
//反序列化,再以對象的方式寫回來,所有的引用類型自然都會帶上了
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
A copyResult = (A)ois.readObject();
return copyResult;
} catch (Exception e) {
e.printStackTrace();
}finally {
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
最終效果也是隨着A的clone,A中的B引用也被clone出新的一份來了。
上面的這個序列化只是一種實現思路,也可以用fastjson將A對象轉成json字符串,再反轉成A返回去,其實原理都是一樣的。
實際應用場景
其實很好找,只要找到哪些類實現了Cloneable接口,大概率就是用到了所謂了原型模式。
比如Spring的Bean的創建,我都都知道默認是單例模式,那其實還可以知道Bean是多例的,這個就用到了原型模式。