文章目錄
- 引例
- 原型模式
- 淺拷貝
- 深拷貝
引例
標題無意冒犯
在介紹原型模式前,我們先從實際問題出發,對比解決方法前后優劣點,

問題:
現在有一只羊(包含屬性:名字Dolly、年齡2),需要克隆10只屬性完全相同的羊,
一般解法:
- 定義Sheep類表示羊,包括構造器、getter()和toString(),
public class Sheep {
private String name;
private int age;
public Sheep(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 在客戶端實體化多利,然后再根據多利的屬性去實體化10只羊,
public class Client {
public static void main(String[] args) {
Sheep sheepDolly=new Sheep("Dolly",2);
Sheep sheep1 = new Sheep(sheepDolly.getName(), sheepDolly.getAge());
Sheep sheep2 = new Sheep(sheepDolly.getName(), sheepDolly.getAge());
Sheep sheep3 = new Sheep(sheepDolly.getName(), sheepDolly.getAge());
//....
System.out.println(sheep1+",hashCode:"+sheep1.hashCode());
System.out.println(sheep2+",hashCode:"+sheep2.hashCode());
System.out.println(sheep3+",hashCode:"+sheep3.hashCode());
//...
}
}
- 運行結果

優缺點:
這種方法是我們首先很容易就能想到的,也是絕大多數人的第一做法,
但缺點也很明顯,每次創建新物件時需要獲取原始物件的屬性,物件復雜時效率很低;此外不能動態獲得物件運行時的狀態,若類增減屬性需要改動代碼,
下面我們看下原型模式的解法,
原型模式
原型模式(Prototype Pattern)是一種創建型設計模式,允許一個物件再創建另外一個可定制的物件,無需知道如何創建的細節,即用原型實體指定創建物件的種類,并且通過拷貝這些原型,創建新的物件,
作業原理:將原型物件傳給那個要發動創建的物件,這個要發動創建的物件通過請求原型物件拷貝它們自己來實施創建,即用基類Object的clone()方法或序列化,
UML類圖:

- Prototype:原型類,宣告一個克隆自己的介面
- ConcretePrototype: 具體的原型類, 實作一個克隆自己的操作
- Client: 客戶端讓一個原型物件克隆自己,從而創建一個新的物件
原型模式又可分為淺拷貝和深拷貝,區別在于對參考資料型別的成員變數的拷貝,小朋友你是否有很多問號? 不急 ,看完這兩種方法實作你就懂了,

淺拷貝
- 在原先Sheep類基礎上實作Cloneable介面,重寫clone方法,
public class Sheep implements Cloneable{
private String name;
private int age;
@Override
protected Object clone() {//克隆該實體,使用默認的clone方法來完成
Sheep sheep = null;
try {
sheep = (Sheep)super.clone();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return sheep;
}
public Sheep(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 客戶端呼叫
public class Client {
public static void main(String[] args) {
Sheep sheepDolly=new Sheep("Dolly",2);
Sheep sheep1 = (Sheep)sheepDolly.clone();
Sheep sheep2 = (Sheep)sheepDolly.clone();
Sheep sheep3 = (Sheep)sheepDolly.clone();
//....
System.out.println("sheep1:"+sheep1+",hashCode:" + sheep1.hashCode());
System.out.println("sheep2:"+sheep2+",hashCode:" + sheep2.hashCode());
System.out.println("sheep3:"+sheep3+",hashCode:" + sheep3.hashCode());
//...
}
}
- 運行結果

至此,原型模式的淺拷貝也成功克隆了三個物件,但是看進度條發現并不簡單,

現在小羊有了一個朋友小牛,Sheep類添加了一個參考屬性Cow,我們同樣再克隆一遍,
- Sheep類
public class Sheep implements Cloneable{
private String name;
private int age;
public Cow friend;//新朋友Cow物件,其余不變
@Override
protected Object clone() {
Sheep sheep = null;
try {
sheep = (Sheep)super.clone();
} catch (Exception e) {
System.out.println(e.getMessage());
}
return sheep;
}
public Sheep(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 新添的Cow類
public class Cow {
private String name;
private int age;
public Cow(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Cow{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
(
插播反爬資訊)博主CSDN地址:https://wzlodq.blog.csdn.net/
- 客戶端呼叫克隆
public class Client {
public static void main(String[] args) {
Sheep sheepDolly=new Sheep("Dolly",2);
sheepDolly.friend=new Cow("Tom",1); //并實體化朋友
Sheep sheep1 = (Sheep)sheepDolly.clone();
Sheep sheep2 = (Sheep)sheepDolly.clone();
Sheep sheep3 = (Sheep)sheepDolly.clone();
//....
System.out.println("sheep1:"+sheep1+",hashCode:" + sheep1.hashCode());
System.out.println("sheep1.friend:"+sheep1.friend+",hashCode:" + sheep1.friend.hashCode()+'\n');
System.out.println("sheep2:"+sheep2+",hashCode:" + sheep2.hashCode());
System.out.println("sheep2.friend:"+sheep2.friend+",hashCode:" + sheep2.friend.hashCode()+'\n');
System.out.println("sheep3:"+sheep3+",hashCode:" + sheep3.hashCode());
System.out.println("sheep3.friend:"+sheep3.friend+",hashCode:" + sheep3.friend.hashCode()+'\n');
//...
}
}
- 運行結果

通過運行結果發現,淺拷貝通過Object的clone()成功克隆實體化了三個新物件,但是并沒有克隆實體化物件中的參考屬性,也就是沒有克隆friend物件(禁止套娃 ),三個新克隆物件的friend還是指向原克隆前的friend,即時同一個物件,
這樣的話,他們四個的friend是參考同一個,若一個物件修改了friend屬性,勢必會影響其他三個物件的該成員變數值,
小結:
- 淺拷貝是使用默認的 clone()方法來實作
- 基本資料型別的成員變數,淺拷貝會直接進行值傳遞(復制屬性值給新物件),
- 參考資料型別的成員變數,淺拷貝會進行參考傳遞(復制參考值(記憶體地址)給新物件),
深拷貝
方法一:
機靈的人兒看出,再clone一遍cow不就好了,但是手動遞回下去不推薦,
- Cow類也實作Cloneable介面
public class Cow implements Cloneable{
private String name;
private int age;
public Cow(String name, int age) {
this.name = name;
this.age = age;
}
//無參考型別,直接clone即可
@Override
protected Object clone() throws CloneNotSupportedException {
return super.clone(); //直接拋出了,沒用try-catch
}
@Override
public String toString() {
return "Cow{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- Sheep類的clone再添加呼叫cow的clone
public class Sheep implements Cloneable{
private String name;
private int age;
public Cow friend;//新朋友Cow物件,其余不變
@Override
protected Object clone() throws CloneNotSupportedException {
Object deep = null;
//完成對基本資料型別(屬性)和String的克隆
deep = super.clone();
//對參考型別的屬性,進行再次clone
Sheep sheep = (Sheep)deep;
sheep.friend = (Cow)friend.clone();
return sheep;
}
public Sheep(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 客戶端呼叫
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheepDolly=new Sheep("Dolly",2);
sheepDolly.friend=new Cow("Tom",1); //并實體化朋友
Sheep sheep1 = (Sheep)sheepDolly.clone();
Sheep sheep2 = (Sheep)sheepDolly.clone();
Sheep sheep3 = (Sheep)sheepDolly.clone();
//....
System.out.println("sheep1:"+sheep1+",hashCode:" + sheep1.hashCode());
System.out.println("sheep1.friend:"+sheep1.friend+",hashCode:" + sheep1.friend.hashCode()+'\n');
System.out.println("sheep2:"+sheep2+",hashCode:" + sheep2.hashCode());
System.out.println("sheep2.friend:"+sheep2.friend+",hashCode:" + sheep2.friend.hashCode()+'\n');
System.out.println("sheep3:"+sheep3+",hashCode:" + sheep3.hashCode());
System.out.println("sheep3.friend:"+sheep3.friend+",hashCode:" + sheep3.friend.hashCode()+'\n');
//...
}
}
- 運行結果

方法二:
通過物件序列化實作深拷貝(推薦)
- Cow類實作序列化介面,不必實作Cloneable介面了
public class Cow implements Serializable {
private String name;
private int age;
public Cow(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Cow{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
- 在Sheep類實作序列化介面
public class Sheep implements Serializable { //實作序列化介面
private String name;
private int age;
public Cow friend;
public Sheep(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Sheep{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Object deepClone() { //深拷貝
//創建流物件
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
//序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this); //當前這個物件以物件流的方式輸出
//反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
Sheep sheep = (Sheep) ois.readObject();
return sheep;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
//關閉流
try {
bos.close();
oos.close();
bis.close();
ois.close();
} catch (Exception e2) {
System.out.println(e2.getMessage());
}
}
}
}
- 客戶端呼叫
public class Client {
public static void main(String[] args) throws CloneNotSupportedException {
Sheep sheepDolly=new Sheep("Dolly",2);
sheepDolly.friend=new Cow("Tom",1); //并實體化朋友
Sheep sheep1 = (Sheep)sheepDolly.deepClone();
Sheep sheep2 = (Sheep)sheepDolly.deepClone();
Sheep sheep3 = (Sheep)sheepDolly.deepClone();
//....
System.out.println("sheep1:"+sheep1+",hashCode:" + sheep1.hashCode());
System.out.println("sheep1.friend:"+sheep1.friend+",hashCode:" + sheep1.friend.hashCode()+'\n');
System.out.println("sheep2:"+sheep2+",hashCode:" + sheep2.hashCode());
System.out.println("sheep2.friend:"+sheep2.friend+",hashCode:" + sheep2.friend.hashCode()+'\n');
System.out.println("sheep3:"+sheep3+",hashCode:" + sheep3.hashCode());
System.out.println("sheep3.friend:"+sheep3.friend+",hashCode:" + sheep3.friend.hashCode()+'\n');
//...
}
}
- 運行結果

原型模式總結:
- 創建新的物件比較復雜時,可以利用原型模式簡化物件的創建程序,同時也能夠提
高效率 - 可以不用重新初始化物件,動態地獲得物件運行時的狀態,
- 如果原始物件發生變化(增加或者減少屬性),其它克隆物件的也會發生相應的變化,無需修改代碼
- 若成員變數無參考型別,淺拷貝clone即可;若參考型別的成員變數很少,可考慮遞回實作clone,否則推薦序列化,
原創不易,請勿轉載(
本不富裕的訪問量雪上加霜)
博主首頁:https://wzlodq.blog.csdn.net/
微信公眾號:唔仄lo咚鏘
如果文章對你有幫助,記得一鍵三連?
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/257093.html
標籤:java
