模式定義
用原型實例指定創(chuàng)建對象的種類闷愤,并通過拷貝這些原型創(chuàng)建新的對象缴川。
模式結(jié)構(gòu)
模式結(jié)構(gòu)
代碼實現(xiàn)
public interface Prototype {
Prototype clone();
}
public class ConcretePrototype1 implements Prototype {
public Prototype clone() {
return new ConcretePrototype1();
}
}
public class ConcretePrototype2 implements Prototype {
public Prototype clone() {
return new ConcretePrototype2();
}
}
@AllArgsConstructor
public class Client {
private Prototype prototype;
public void operation(){
Prototype newPrototype = prototype.clone();
}
}
模式的優(yōu)缺點
優(yōu)點
創(chuàng)建對象的性能高
使用原型模式創(chuàng)建對象比直接new一個對象在性能上要好的多瘤旨,因為Object類的clone方法是一個本地方法烟很,它直接操作內(nèi)存中的二進制流,特別是復(fù)制大對象時蜓耻,性能的差別非常明顯茫舶。簡化對象的創(chuàng)建
缺點
克隆的對象的成員變量包含引用類型(String除外)需要特殊處理
逃避構(gòu)造方法的約束
必須實現(xiàn)Cloneable接口
深拷貝和淺拷貝
發(fā)生深拷貝的有Java中的8中基本類型以及它們包裝類型,另外還有String類型刹淌。其余的都是淺拷貝饶氏。
淺拷貝
對值類型的成員變量進行值的復(fù)制,對引用類型的成員變量只復(fù)制引用,不復(fù)制引用的對象有勾。
@Data
@AllArgsConstructor
public class Student implements Cloneable{
private String studentName;
private Teacher teacher;
@Override
protected Student clone() throws CloneNotSupportedException {
return (Student) super.clone();
}
}
@Data
public class Teacher implements Serializable, Cloneable {
private static final long UID = 6948989635489677685L;
private String name;
public Teacher(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
Teacher teacher;
teacher = (Teacher) super.clone();
return teacher;
}
}
@Data
public class StudentLow implements Serializable, Cloneable {
private static final long UID = 6948989635489677685L;
private String studentName;
private Teacher teacher;
public StudentLow(String studentName, Teacher teacher) {
this.studentName = studentName;
this.teacher = teacher;
}
@Override
protected Object clone() throws CloneNotSupportedException {
StudentLow student;
student = (StudentLow) super.clone();
return student;
}
}
public class Client {
public static void main(String[] args) throws Exception {
Teacher teacher = new Teacher("snail");
StudentLow student1 = new StudentLow("wjk", teacher);
StudentLow student2 = (StudentLow) student1.clone();
student2.getTeacher().setName("snail改變");
System.out.println(student1.getTeacher().getName());
System.out.println(student2.getTeacher().getName());
}
}
//運行結(jié)果(修改克隆的Student的成員變量Teacher的成員變量name導(dǎo)致被克隆的對象的也改變)
snail
snail改變
深拷貝
對值類型的成員變量進行值的復(fù)制疹启,對引用類型的成員變量也進行引用對象的復(fù)制。
//將上面代碼StudentLow類使用StudentDeep類替換
@Data
public class StudentDeep implements Serializable, Cloneable {
private static final long UID = 6948989635489677685L;
private String studentName;
private Teacher teacher;
public StudentDeep(String studentName, Teacher teacher) {
this.studentName = studentName;
this.teacher = teacher;
}
@Override
protected Object clone() throws CloneNotSupportedException {
StudentDeep student;
student = (StudentDeep) super.clone();
//繼續(xù)克隆Teacher
student.setTeacher((Teacher) this.teacher.clone());
return student;
}
/**
* 使用序列化實現(xiàn)深拷貝
*/
/*public Object deepClone() {
try {
//將對象寫到流里
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream oo = new ObjectOutputStream(bo);
oo.writeObject(this);
//從流里讀出來
ByteArrayInputStream bi = new ByteArrayInputStream(bo.toByteArray());
ObjectInputStream oi = new ObjectInputStream(bi);
return (oi.readObject());
} catch (Exception e) {
return null;
}
}*/
}
思考
模式本質(zhì):克隆生成對象柠衅。
開發(fā)中的應(yīng)用場景
如果一個系統(tǒng)想要獨立于它想要使用的對象時皮仁,可以使用原型模式,讓系統(tǒng)只面向接口編程菲宴,在系統(tǒng)需要新的對象的時候,可以通過克隆來得到趋急。
如果需要實例化的類事在運行時刻動態(tài)指定是喝峦,可以使用原型模式,通過克隆原型來得到需要的實例呜达。
注意
- 使用原型模式不會調(diào)用類的構(gòu)造方法.
對象的復(fù)制是通過調(diào)用Object類的clone方法來完成的谣蠢,它直接在內(nèi)存中復(fù)制數(shù)據(jù),因此不會調(diào)用到類的構(gòu)造方法。不但構(gòu)造方法中的代碼不會執(zhí)行眉踱,甚至連訪問權(quán)限都對原型模式無效挤忙。還記得單例模式嗎?單例模式中谈喳,只要將構(gòu)造方法的訪問權(quán)限設(shè)置為private型册烈,就可以實現(xiàn)單例。但是clone方法直接無視構(gòu)造方法的權(quán)限婿禽,所以赏僧,單例模式與原型模式是沖突的,在使用時要特別注意扭倾。