【设计模式】原型模式 ( 浅拷贝 | 深拷贝 | 原型与单例冲突 | 禁用 final )
文章目錄
- I . 原型模式 總結
- II . 原型模式 淺拷貝
- III . 原型模式 深拷貝
- IV . 原型模式 與 單例
- V . 原型模式 中的 final 關鍵字 ( 禁止出現 )
I . 原型模式 總結
1 . 原型模式本質及性能 : 原型模式使用 clone 方法克隆對象 , 其本質是在內存中拷貝二進制數據 , 這種方式要比調用 new 構造函數性能高得多 ;
2 . clone 核心是內存拷貝 : clone 對象不使用復用原有對象 , 是在內存中的另一個地址空間復制了一份一模一樣的數據 , 然后將其首地址給新對象的引用 ;
3 . 原型模式適用場景 : ① 節省資源 ( 內存 CPU 硬件等 ) , ② 構造函數復雜 ( 計算繁瑣 耗時 ) , ③ 創建大量對象 ;
4 . 原型模式實現 : 原型模式類實現 Cloneable 接口 , 實現其中的 clone 方法 ;
① 淺拷貝實現 : 淺拷貝默認調用 super.clone ;
② 深拷貝實現 : 深拷貝需要調用 每個引用成員對象的 clone 方法創建成員對象 , 然后賦值給新的原型模式創建的對象 , 作為其中的成員 ;
5 . 注意拷貝方式 : 默認淺拷貝 , 如果類中有引用類型成員變量 , 需要考慮深拷貝問題 , 可能會出現多個對象持有同一個引用變量 ;
II . 原型模式 淺拷貝
1 . 淺拷貝 : 調用 clone 對象拷貝內存中的數據時 , 要注意拷貝的是基礎數據類型 , 對于數組 , 集合 , 自定義類等引用數據類型僅拷貝地址 , 會造成所有的對象都持有同一個內存地址的引用成員 ;
① 基礎數據類型 : 如果類中全部是基礎數據類型 , 使用 clone 可以將該類完整的復制一份 ;
② 引用數據類型 : 如果類中有引用類型成員 , 只是拷貝該成員的地址 , 所有的拷貝創建的原型模式實例對象都持有同一個引用 , 如果修改該引用成員的值 , 所有的原型對象實例的值都會跟著修改 ;
2 . 淺拷貝示例 :
① 原型模式類 Student : 該類中持有 Vector<String> courses 引用數據類型 , 調用 clone 方法在內存中復制對象時 , 僅復制了對象的地址 , 即將該引用的地址賦值給了 clone 出的對象 ;
package kim.hsl.design.prototype.shallowcopy;import java.util.Vector;/*** 淺拷貝示例*/ public class Student implements Cloneable {private String name;private int age;//該類在拷貝時 , 如果使用淺拷貝 , 只是將地址拷貝走了 , 兩個對象實際上用的是同一個對象private Vector<String> courses = new Vector<>();public Student() {System.out.println("調用 Student 默認構造函數");}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Vector<String> getCourses() {return courses;}public void setCourses(Vector<String> courses) {this.courses = courses;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", courses=" + courses +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {System.out.println("調用 Student clone 方法");return super.clone();} }② 測試類 Main : 此處創建了兩個 Student 實例對象 , 但是兩個對象都持有同一個 Vector<String> courses 引用數據類型成員 , 當修改其中一個成員時 , 兩個對象中的該成員都會改變 ;
package kim.hsl.design.prototype.shallowcopy;public class Main {public static void main(String[] args) {try {//測試使用 clone 方法實現的原型模式 , 使用原型模式創建 2 個對象Student newStudent = new Student();// 1 . 使用 clone 方法創建對象1Student student = (Student) newStudent.clone();student.setName("Tom");student.setAge(10);student.getCourses().add("數學");// 2 . 使用 clone 方法創建對象2Student student2 = (Student) newStudent.clone();student2.setName("Jerry");student2.setAge(18);student2.getCourses().add("語文");System.out.println("student : " + student + "\nstudent2 : " + student2);} catch (CloneNotSupportedException e) {//捕獲 clone 方法可能產生的異常e.printStackTrace();}} }③ 執行結果 : 調用第一個對象 add 方法 , 在 vector 集合中添加了 “數學” 字符串 , 調用第二個對象的 add 方法 , 向 courses 集合中添加 “語文” 字符串 , 理論上兩個分別是 “數學” 和 “語文” , 但是此處卻都變成了 “數學” “語文” 兩個課程 , 說明兩個原型模式對象持有的 Vector<String> courses 變量是指向同一個內存地址的 ;
調用 Student 默認構造函數 調用 Student clone 方法 調用 Student clone 方法 student : Student{name='Tom', age=10, courses=[數學, 語文]} student2 : Student{name='Jerry', age=18, courses=[數學, 語文]}III . 原型模式 深拷貝
1 . 深拷貝策略 : 深拷貝時需要在 clone 方法中 , 調用引用數據類型本身的 clone 對象 , 在將其賦值給被拷貝的原型模式實例對象 ;
2 . 深拷貝 clone 方法流程 :
① 創建實例對象 : 通過 clone 方法 , 創建原型模式類的實例對象 , 此時該對象的引用成員處于淺拷貝狀態 ;
② 拷貝引用成員 : 調用原型模式類對象成員的 clone 對象 , 創建新的成員對象 , 將新的成員對象賦值給克隆出的原型模式對象 ;
③ 返回新的對象 : 返回 clone 創建的原型模式實例對象 ;
3 . 示例代碼 :
① 原型模式深拷貝示例 : 深拷貝與淺拷貝只是在 clone 方法中表現不同 , 其它代碼一致 ; 在 clone 方法中需要針對引用類型進行克隆 ;
package kim.hsl.design.prototype.deepcopy;import java.util.Vector;/*** 淺拷貝示例*/ public class Student implements Cloneable {private String name;private int age;//該類在拷貝時 , 如果使用淺拷貝 , 只是將地址拷貝走了 , 兩個對象實際上用的是同一個對象private Vector<String> courses = new Vector<>();public Student() {System.out.println("調用 Student 默認構造函數");}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Vector<String> getCourses() {return courses;}public void setCourses(Vector<String> course) {this.courses = course;}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +", course=" + courses +'}';}@Overrideprotected Object clone() throws CloneNotSupportedException {System.out.println("調用 Student clone 方法");//1 . 首先拷貝一個基本對象Student student = (Student) super.clone();//2 . 將引用類型的對象單獨克隆賦值student.courses = (Vector<String>) this.courses.clone();//3 . 返回創建的新的對象return student;} }② 測試代碼 :
package kim.hsl.design.prototype.deepcopy;public class Main {public static void main(String[] args) {try {//測試使用 clone 方法實現的原型模式 , 使用原型模式創建 2 個對象Student newStudent = new Student();// 1 . 使用 clone 方法創建對象1Student student = (Student) newStudent.clone();student.setName("Tom");student.setAge(10);student.getCourses().add("數學");// 2 . 使用 clone 方法創建對象2Student student2 = (Student) newStudent.clone();student2.setName("Jerry");student2.setAge(18);student2.getCourses().add("語文");System.out.println("student : " + student + "\nstudent2 : " + student2);} catch (CloneNotSupportedException e) {//捕獲 clone 方法可能產生的異常e.printStackTrace();}} }③ 運行結果 : 原型模式的兩個實例對象的互不干擾 ;
調用 Student 默認構造函數 調用 Student clone 方法 調用 Student clone 方法 student : Student{name='Tom', age=10, course=[數學]} student2 : Student{name='Jerry', age=18, course=[語文]}
IV . 原型模式 與 單例
1 . 原型模式 與 單例模式 :
① 原型模式 : 原型模式的核心是不調用構造函數 , 使用 clone 方法在內存中克隆對象 ;
② 單例模式 : 單例模式的核心是私有化構造函數 , 控制外部用戶 , 不能隨意調用構造函數創建對象 ;
2 . Cloneable 破壞了單例模式 : 此處二者就出現了矛盾 , 如果單例類 , 實現了 Cloneable 接口 , 那么該類就可以調用 clone 方法創建另外的實例對象 , 因此破壞了單例模式 ;
3 . 解決方案 :
① 不實現 Cloneable 接口 : 單例模式中不要實現 Cloneable 接口 , 不提供內存拷貝功能 ;
② clone 中調用單例 : 如果必須實現 Cloneable 接口 , 那么在重寫的 clone 方法中 , 調用獲取單例類的方法 , 不要進行內存對象拷貝創建新的實例對象 ;
V . 原型模式 中的 final 關鍵字 ( 禁止出現 )
1 . final 作用 : final 用于修飾常量 , 被 final 修飾的變量無法重新賦值 ;
2 . Cloneable 實現類成員不要使用 final : 在原型模式的類中 , 實現了 Cloneable 接口 , 重寫了 clone 方法 , 其類對象的成員不能被 final 修飾 , 否則無法重新賦值 ;
總結
以上是生活随笔為你收集整理的【设计模式】原型模式 ( 浅拷贝 | 深拷贝 | 原型与单例冲突 | 禁用 final )的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【设计模式】原型模式 ( 概念简介 |
- 下一篇: 【设计模式】外观模式 ( 概念 | 适用