java設計模式-----5、原型模式


  原型(Prototype)模式是一種對象創建型模式,他采取復制原型對象的方法來創建對象的實例。使用原型模式創建的實例,具有與原型一樣的數據。

  原型模式的特點:

  1、由原型對象自身創建目標對象。也就是說,對象創建這一動作發自原型對象本身。

  2、目標對象是原型對象的一個克隆。也就是說,通過原型模式創建的對象,不僅僅與原型對象具有相同的結構,還與原型對象具有相同的值。

  3、根據對象克隆深度層次的不同,有淺度克隆與深度克隆。

 

  先寫一個支持克隆的類

 1 //如果要克隆就必須實現Cloneable接口
 2 public class Person implements Cloneable{
 3     //可能會拋出不支持克隆異常,原因是沒有實現Cloneable接口
 4     @Override
 5     protected Person clone(){
 6         try{
 7             return (Person) super.clone();
 8         }catch(CloneNotSupportedException e){
 9             e.printStackTrace();
10             return null;
11         }
12     }
13 }

  這個樣子,就說明這個類可以克隆了。

  這樣克隆

1 public class MainClass {
2     public static void main(String[] args) {
3         Person person1 = new Person();
4         
5         Person person2 = person1.clone();
6     }
7 }

  這樣子克隆並不等同於Person p2 = p1;像Person p2 = p1;指的是在棧中創建一個變量p2,將p1的內存地址賦給p2,其實指的是同一個對象。而克隆是復制出一份一模一樣的對象,兩個對象內存地址不同,但對象中的結構與屬性值一模一樣。

  這種不通過 new 關鍵字來產生一個對象,而是通過對象拷貝來實現的模式就叫做原型模式,這個模式的核心是一個clone( )方法,通過這個方法進行對象的拷貝,Java 提供了一個 Cloneable 接口來標示這個對象是可拷貝的,為什么說是“標示”呢?翻開 JDK 的幫助看看 Cloneable 是一個方法都沒有的,

這個接口只是一個標記作用,在 JVM 中具有這個標記的對象才有可能被拷貝,所以覆蓋了覆蓋clone()方法就可以了。

  在 clone()方法上增加了一個注解@Override, 沒有繼承一個類為什么可以重寫呢?在 Java 中所有類的父類是Object 類,每個類默認都是繼承了這個類,所以這個用上@Override是非常正確的。原型模式雖然很簡單,但是在 Java 中使用原型模式也就是 clone 方法還是有一些注意事項的:  

  對象拷貝時,類的構造函數是不會被執行的 一個實現了 Cloneable 並重寫了 clone 方法的類 A,有一個無參構造或有參構造 B,通過 new 關鍵字產生了一個對象 S,再然后通過 S.clone()方式產生了一個新的對象 T,那么在對象拷貝時構造函數 B 是不會被執行的, 對象拷貝時確實構造函數沒有被執行,這個從原理來講也是可以講得通的,Object 類的 clone 方法的 原理是從內存中(具體的說就是堆內存)以二進制流的方式進行拷貝,重新分配一個內存塊,那構造函數 沒有被執行也是非常正常的了。

  還有就是深度克隆與淺度克隆

  首先,是淺度克隆

 1 //如果要克隆就必須實現Cloneable接口
 2 public class Person implements Cloneable{
 3     private String name;
 4     private String sex;
 5     private List<String> list;
 6     public String getName() {
 7         return name;
 8     }
 9     public void setName(String name) {
10         this.name = name;
11     }
12     public String getSex() {
13         return sex;
14     }
15     public void setSex(String sex) {
16         this.sex = sex;
17     }
18     public List<String> getList() {
19         return list;
20     }
21     public void setList(List<String> list) {
22         this.list = list;
23     }
24     //可能會拋出不支持克隆異常,原因是沒有實現Cloneable接口
25     @Override
26     protected Person clone(){
27         try{
28             return (Person) super.clone();
29         }catch(CloneNotSupportedException e){
30             e.printStackTrace();
31             return null;
32         }
33     }
34 }

   這就是淺度克隆,當被克隆的類中有引用對象(String或Integer等包裝類型除外)時,克隆出來的類中的引用變量存儲的還是之前的內存地址,也就是說克隆與被克隆的對象是同一個。這樣的話兩個對象共享了一個私有變量,所有人都可以改,是一個種非常不安全的方式,在實際項目中使用還是比較少的。

  所以就要說到深度拷貝

 1 //如果要克隆就必須實現Cloneable接口
 2 public class Person implements Cloneable{
 3     private String name;
 4     private String sex;
 5     private List<String> list;
 6     public String getName() {
 7         return name;
 8     }
 9     public void setName(String name) {
10         this.name = name;
11     }
12     public String getSex() {
13         return sex;
14     }
15     public void setSex(String sex) {
16         this.sex = sex;
17     }
18     public List<String> getList() {
19         return list;
20     }
21     public void setList(List<String> list) {
22         this.list = list;
23     }
24     //可能會拋出不支持克隆異常,原因是沒有實現Cloneable接口
25     @Override
26     protected Person clone(){
27         try{
28             Person person = (Person) super.clone();
29             List<String> newList = new ArrayList();
30             
31             for(String str : this.list){
32                 newList.add(str);
33             }
34             person.setList(newList);
35             return person;
36         }catch(CloneNotSupportedException e){
37             e.printStackTrace();
38             return null;
39         }
40     }
41 }

  這樣就完成了深度拷貝,兩種對象互為獨立,屬於單獨對象。

  注意:final 類型修飾的成員變量不能進行深度拷貝  

  最后說一下,原型模式的使用場景

  1、在創建對象的時候,我們不只是希望被創建的對象繼承其基類的基本結構,還希望繼承原型對象的數據。

  2、希望對目標對象的修改不影響既有的原型對象(深度克隆的時候可以完全互不影響)。

  3、隱藏克隆操作的細節,很多時候,對對象本身的克隆需要涉及到類本身的數據細節。

  4、類初始化需要消化非常多的資源,這個資源包括數據、硬件資源等;

  5、通過 new 產生一個對象需要非常繁瑣的數據准備或訪問權限,則可以使用原型模式;

  6、一個對象需要提供給其他對象訪問,而且各個調用者可能都需要修改其值時,可以考慮使用原型模式拷貝多個對象供調用者使用。在實際項目中,原型模式很少單獨出現,一般是和工廠方法模式一起出現,通過 clone的方法創建一個對象,然后由工廠方法提供給調用者。原型模式先產生出一個包含

  大量共有信息的類,然后可以拷貝出副本,修正細節信息,建立了一個完整的個性對象。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM