1. 程式人生 > >java23種設計模式——四、原型模式

java23種設計模式——四、原型模式

原始碼在我的[github](https://github.com/witmy/JavaDesignPattern)和[gitee](https://gitee.com/witmy/JavaDesignPattern)中獲取 # 目錄 [java23種設計模式—— 一、設計模式介紹](https://www.cnblogs.com/codermy/p/13565137.html) [java23種設計模式—— 二、單例模式](https://www.cnblogs.com/codermy/p/13566365.html) [java23種設計模式——三、工廠模式](https://www.cnblogs.com/codermy/p/13569486.html) [java23種設計模式——四、原型模式](https://www.cnblogs.com/codermy/p/13571750.html) # java23種設計模式——四、原型模式 > 這種模式是實現了一個原型介面,該介面用於建立當前物件的克隆。當直接建立物件的代價比較大時,則採用這種模式。例如,一個物件需要在一個高代價的資料庫操作之後被建立。我們可以快取該物件,在下一個請求時返回它的克隆,在需要的時候更新資料庫,以此來減少資料庫呼叫。 舉個例子,就是當我們需要給電腦安裝win10系統時需要去官網上下載win10系統的安裝包。而安裝包的大小都是很耗時的,還需要另一臺電腦來操作。如果我們下載了一個安裝包放在我們的u盤裡,之後需要安裝win10時是不是就省去了中間尋找,下載等時間呢 原型模式的克隆分為淺克隆和深克隆,Java 中的 Object 類提供了淺克隆的 clone() 方法,具體原型類只要實現 Cloneable 介面就可實現物件的淺克隆,這裡的 Cloneable 介面就是抽象原型類。其程式碼如下 ## 淺克隆 新建一個實體類Sheep實現Cloneable 介面,重寫clone()方法 ```java /** * @author codermy * @createTime 2020/5/14 */ public class Sheep implements Cloneable{ private String name; private int age; private String sex; 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 String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Sheep(String name, int age, String sex) { this.name = name; this.age = age; this.sex = sex; } @Override public String toString() { return "Sheep{" + "name='" + name + '\'' + ", age=" + age + ", sex='" + sex + '\'' + '}'; } //克隆該例項,使用預設的clone方法 @Override protected Object clone() throws CloneNotSupportedException { Sheep sheep =null; sheep = (Sheep)super.clone(); return sheep; } } ``` 測試 ```java /** * @author codermy * @createTime 2020/5/14 */ public class Client { public static void main(String[] args) throws CloneNotSupportedException { Sheep sheep = new Sheep("tom",1,"male"); Sheep sheep1 = (Sheep)sheep.clone(); System.out.println(sheep.hashCode()); System.out.println(sheep); System.out.println(sheep1.hashCode()); System.out.println(sheep1); sheep1.setAge(2); System.out.println(sheep1); System.out.println(sheep); } } ``` 輸出 ```bash 1163157884 Sheep{name='tom', age=1, sex='male'} 1956725890 Sheep{name='tom', age=1, sex='male'} Sheep{name='tom', age=2, sex='male'} Sheep{name='tom', age=1, sex='male'} ``` 在淺克隆中,被複制物件的所有普通成員變數都具有與原來物件相同的值,而所有的對其他物件的引用仍然指向原來的物件。也就是說,淺克隆僅僅複製所考慮的物件,不會複製它所引用的成員物件。 我們先新建一個Pearson類,作為物件屬性 ```java /** * @author codermy * @createTime 2020/7/24 */ public class Person implements Cloneable{ String name; public String getName() { return name; } public void setName(String name) { this.name = name; } public Person(){ } public Person(String name){ this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } ``` 我們先給Sheep實體類種新增一個物件屬性 ```java /** * @author codermy * @createTime 2020/6/16 */ public class Sheep implements Cloneable { private String name; private int age; private String sex; public Person owner;//物件引用 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 String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Person getOwner() { return owner; } public void setOwner(Person owner) { this.owner = owner; } public Sheep(String name, int age, String sex, Person owner) { this.name = name; this.age = age; this.sex = sex; this.owner = owner; } @Override public String toString() { return "Sheep{" + "name='" + name + '\'' + ", age=" + age + ", sex='" + sex + '\'' + ", owner=" + owner + '}'; } //克隆該例項,使用預設的clone方法 @Override protected Object clone() throws CloneNotSupportedException { Sheep sheep =null; sheep = (Sheep)super.clone(); return sheep; } } ``` 測試類中測試 ```java /** * @author codermy * @createTime 2020/6/16 */ public class Client { public static void main(String[] args) throws CloneNotSupportedException { Person owner = new Person("馬雲"); Sheep sheep = new Sheep("tom",1,"male", owner);//新建sheep類 Sheep sheep1 = (Sheep)sheep.clone();//克隆該類 System.out.println(sheep.hashCode() + " " + sheep.owner.hashCode()); System.out.println(sheep + " "+ sheep.owner); System.out.println(sheep1.hashCode()+ " " + sheep1.owner.hashCode()); System.out.println(sheep1 + " " + sheep1.owner); sheep1.owner.setName("馬化騰"); System.out.println(sheep.owner); System.out.println(sheep1.owner); } } ``` 輸出 ```java 1163157884 1956725890 Sheep{name='tom', age=1, sex='male', owner=Person{name='馬雲'}} Person{name='馬雲'} 356573597 1956725890 Sheep{name='tom', age=1, sex='male', owner=Person{name='馬雲'}} Person{name='馬雲'} Person{name='馬化騰'} Person{name='馬化騰'} ``` 我們可以看出淺克隆時物件的引用僅僅是指向了原空間,而並沒有複製物件。 ## 深克隆 在深克隆中,對值型別的成員變數進行值的複製,對引用型別的成員變數也進行引用物件的複製。 ### 自定義clone過程實現深克隆 將上面Sheep類中的clone方法改寫 ```java @Override protected Object clone() throws CloneNotSupportedException { Sheep sheep =null; sheep = (Sheep)super.clone(); sheep.owner = (Person) sheep.owner.clone();//引用物件的克隆方法 return sheep; } ``` 測試類測試 ```java public class Client { public static void main(String[] args) throws CloneNotSupportedException { Person owner = new Person("馬雲"); Sheep sheep = new Sheep("tom",1,"male", owner); Sheep sheep1 = (Sheep)sheep.clone(); System.out.println(sheep.hashCode() + " " + sheep.owner.hashCode()); System.out.println(sheep + " "+ sheep.owner); System.out.println(sheep1.hashCode()+ " " + sheep1.owner.hashCode()); System.out.println(sheep1 + " " + sheep1.owner); sheep1.owner.setName("馬化騰"); System.out.println(sheep.owner); System.out.println(sheep1.owner); } } ``` 輸出 ```bash 1163157884 1956725890 Sheep{name='tom', age=1, sex='male', owner=Person{name='馬雲'}} Person{name='馬雲'} 356573597 1735600054 Sheep{name='tom', age=1, sex='male', owner=Person{name='馬雲'}} Person{name='馬雲'} Person{name='馬雲'} Person{name='馬化騰'} ``` 這時候我們已經實現了深克隆,但是總覺得有點“淺淺克隆”的意思,如果person類中還有物件引用那不就是。。 **禁止套娃** ![](https://img2020.cnblogs.com/blog/2117964/202008/2117964-20200827153606863-1177974586.png) ### 序列化實現深克隆 兩個實體類實現序列化介面 Person類 ```java public class Person implements Serializable { String name; public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + '}'; } public Person(String name){ this.name = name; } } ``` Sheep類 ```java /** * @author codermy * @createTime 2020/6/16 */ public class Sheep implements Serializable { private String name; private int age; private String sex; public Person owner;//物件引用 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 String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public Person getOwner() { return owner; } public void setOwner(Person owner) { this.owner = owner; } public Sheep() { } public Sheep(String name, int age, String sex, Person owner) { this.name = name; this.age = age; this.sex = sex; this.owner = owner; } @Override public String toString() { return "Sheep{" + "name='" + name + '\'' + ", age=" + age + ", sex='" + sex + '\'' + ", owner=" + owner + '}'; } } ``` 實現 ```java ** * @author codermy * @createTime 2020/7/24 */ public class Client { public static void main(String[] args) throws Exception { Person owner = new Person("馬雲"); Sheep sheep = new Sheep("tom",1,"male", owner); ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(sheep); ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray())); Sheep sheep1 =(Sheep) ois.readObject(); bos.flush();oos.flush(); bos.close();oos.close(); ois.close(); System.out.println("Sheep: " + sheep); System.out.println("Sheep1: " + sheep1); System.out.println("================================"); System.out.println("Sheep: " + sheep.hashCode() + "++++++++++" + sheep.owner.hashCode()); System.out.println("Sheep1: " + sheep1.hashCode() + "++++++++++" + sheep1.owner.hashCode()); System.out.println("================================"); sheep1.owner.setName("馬化騰"); System.out.println("Sheep: " + sheep.owner); System.out.println("Sheep1: " + sheep1.owner); } } ``` 輸出 ```bash 1163157884 1956725890 Sheep{name='tom', age=1, sex='male', owner=Person{name='馬雲'}} Person{name='馬雲'} 356573597 1735600054 Sheep{name='tom', age=1, sex='male', owner=Person{name='馬雲'}} Person{name='馬雲'} Person{name='馬雲'} Person{name='馬化騰'} ``` ## 原型模式的優缺點 **優點**:原型模式是在記憶體中二進位制流的拷貝,要比new一個物件的效能要好,特別是需要產生大量物件時。 **缺點**:直接在記憶體中拷貝,建構函式是不會執行的。