指原型實例指定建立對象的種類,而且經過拷貝這些原型建立新的對象java
調用者不須要知道任何建立細節,不調用構造函數app
你必定遇到過大篇幅 getter,setter 賦值的場景.例如這樣的代碼ide
public void setParam(ExamPaperVo vo){ ExamPaper examPaper = new ExamPaper(); //試卷主鍵 examPaper.setExaminationPaperId(vo.getExaminationPaperId()); //剩餘時間 curForm.setLeavTime(examPaper.getLeavTime()); //單位主鍵 curForm.setOrganizationId(examPaper.getOrganizationId()); //考試主鍵 curForm.setId(examPaper.getId()); //考場主鍵 curForm.setExamroomId(examPaper.getExamroomId()); //用戶主鍵 curForm.setUserId(examPaper.getUserId()); //專業 curForm.setSpecialtyCode(examPaper.getSpecialtyCode()); //崗位 curForm.setPostionCode(examPaper.getPostionCode()); //等級 curForm.setGradeCode(examPaper.getGradeCode()); //考試開始時間 curForm.setExamStartTime(examPaper.getExamStartTime()); //考試結束時間 curForm.setExamEndTime(examPaper.getExamEndTime()); //單選題重要數量 curForm.setSingleSelectionImpCount(examPaper.getSingleSelectionImpCount()); //多選題重要數量 curForm.setMultiSelectionImpCount(examPaper.getMultiSelectionImpCount()); //判斷題重要數量 curForm.setJudgementImpCount(examPaper.getJudgementImpCount()); //考試時間 curForm.setExamTime(examPaper.getExamTime()); //總分 curForm.setFullScore(examPaper.getFullScore()); //及格分 curForm.setPassScore(examPaper.getPassScore()); //學員姓名 curForm.setUserName(examPaper.getUserName()); //分數 curForm.setScore(examPaper.getScore()); //是否及格 curForm.setResult(examPaper.getResult()); curForm.setIsPassed(examPaper.getIsPassed()); //單選答對數量 curForm.setSingleOkCount(examPaper.getSingleOkCount()); //多選答對數量 curForm.setMultiOkCount(examPaper.getMultiOkCount()); //判斷答對數量 curForm.setJudgementOkCount(examPaper.getJudgementOkCount()); //提交試卷 service.submit(examPaper); }
代碼很是工整,命名很是規範,註釋也寫的很全面,你們以爲這樣的代碼優雅嗎?我認爲,這樣的代碼屬於純體力勞動.那麼原型模式,能幫助咱們解決這樣的問題函數
原型 prototype 接口源碼分析
public interface Prototype { Prototype clone();
}
須要克隆類測試
@Data @AllArgsConstructor
@NoArgsConstructor
public class ConcretePrototypeA implements Prototype {
/**
* 年齡
*/
private int age;
/**
* 姓名
*/
private String name;
/**
* 興趣愛好
*/
private List hobbies;
@Override
public Prototype clone() {
return new ConcretePrototypeA(this.age, this.name, this.hobbies);
}
}
測試類ui
package com.zhunongyun.toalibaba.designpatterns.prototype.simelp;
import java.util.ArrayList;this
public class PrototypeTest {prototype
public static void main(String[] args) { ConcretePrototypeA concretePrototypeA = new ConcretePrototypeA(18, "test_name", new ArrayList()); ConcretePrototypeA copy = (ConcretePrototypeA) concretePrototypeA.clone(); System.out.println("原對象與克隆對象內存地址對比, 地址是否相同:" + (concretePrototypeA == copy)); System.out.println("原對象與克隆對象中引用類型內存地址對比, 地址是否相同:" + (concretePrototypeA.getHobbies() == copy.getHobbies())); }
}code
運行結果 ![](https://huaweirookie.oss-cn-shenzhen.aliyuncs.com/20200715213356.jpg) 從測試結果看出 hobbies 的引用地址是相同的,意味着複製的不是值,而是引用的地址.這樣的話,若是咱們修改任意一個對象中的屬性值,concretePrototype 和 concretePrototypeCone 的 hobbies 值都會改變.這就是咱們常說的淺克隆.只是完整複製了值類型數據,沒有賦值引用對象 * 淺克隆對於基本數據類型和 String 類型字段會從新申請內存複製數據,克隆對象會指向新的內存地址 * 淺克隆對於引用類型的字段不會從新申請內存,而是把字段的內存地址指向以前原對象字段的內存地址 ## 3.2 深克隆 定義一個孫悟空類,拔一根毫毛吹出千萬個猴子,每一個猴子都有屬於本身的金箍棒 經過字節碼實現深克隆 * 待克隆類 ```java @Data @AllArgsConstructor @NoArgsConstructor public class JinGuBang implements Serializable { private float height = 100; private float wide = 10; public void big() { this.height *= 2; this.wide *= 2; } public void small() { this.height /= 2; this.wide /= 2; } }
@Data public class QiTianDaSheng implements Cloneable, Serializable { private JinGuBang jinGuBang; private int height; private int weight; private Date birthday; public QiTianDaSheng() { this.birthday = new Date(); this.jinGuBang = new JinGuBang(); } @Override protected Object clone() { return this.deepClone(); } public Object deepClone() { ByteArrayOutputStream byteArrayOutputStream = null; ObjectOutputStream objectOutputStream = null; ByteArrayInputStream byteArrayInputStream = null; ObjectInputStream objectInputStream = null; // 內存中完成操做,對象讀寫,是經過字節碼直接操做 // 序列化操做相似 try { byteArrayOutputStream = new ByteArrayOutputStream(); objectOutputStream = new ObjectOutputStream(byteArrayOutputStream); objectOutputStream.writeObject(this); byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray()); objectInputStream = new ObjectInputStream(byteArrayInputStream); // 完整的新的對象, new 出來一個新的對象 QiTianDaSheng copy = (QiTianDaSheng) objectInputStream.readObject(); copy.setBirthday(new Date()); return copy; } catch (Exception e) { e.printStackTrace(); return null; } finally { IOUtils.closeQuietly(objectInputStream); IOUtils.closeQuietly(byteArrayInputStream); IOUtils.closeQuietly(objectOutputStream); IOUtils.closeQuietly(byteArrayOutputStream); } } }
public class DeepCloneTest {
public static void main(String[] args) {
QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
QiTianDaSheng clone = (QiTianDaSheng) qiTianDaSheng.clone(); System.out.println("深克隆,對象中的引用類型字段 JinGuBang,內存地址是否相同:" + (qiTianDaSheng.getJinGuBang() == clone.getJinGuBang())); }
}
運行結果 ![b4d128ce939829eeeff142cf1514531f.png](evernotecid://0B7710CE-D342-4510-A5F9-81F7B62D33F2/wwwevernotecom/149352153/ENResource/p155) # 4. 克隆破壞單例模式 若是咱們克隆的目標的對象是單例對象,那意味着,深克隆就會破壞單例.實際上防止克隆破壞單例解決思路很是簡單,禁止深克隆即可.要麼你咱們的單例類不實現 Cloneable 接口;要麼咱們重寫 clone()方法,在 clone 方法中返回單例對象便可,具體代碼以下 ```java @Override protected Object clone() throws CloneNotSupportedException { return INSTANCE; }
Cloneable 是標記型的接口,它們內部都沒有方法和屬性,實現 Cloneable 來表示該對象能被克隆,能使用 Object.clone()方法
若是沒有實現 Cloneable 的類對象調用 clone()就會拋出 CloneNotSupportedException
在 ArrayList 中存在 clone 方法,但屬於淺克隆
public Object clone() { try { ArrayList<?> v = (ArrayList<?>) super.clone(); v.elementData = Arrays.copyOf(elementData, size); v.modCount = 0; return v; } catch (CloneNotSupportedException e) { // this shouldn't happen, since we are Cloneable throw new InternalError(e); } }