Java 設計模式之原型模式(五)

1、前言

本篇介紹 Java 設計模式中建立型模式的最後一種–原型模式。上篇設計模式主題爲 《Java 設計模式之建造者模式(四)》java

2、簡單介紹

原型模式是一種對象建立型模式,它採起復制原型對象的方法來建立對象的實例。使用原型模式建立的實例,具備與原型同樣的數據。設計模式

2.1 特色

1) 由原型對象自身建立目標對象。即對象建立這一動做發自原型對象自己。ide

2) 目標對象是原型對象的一個克隆。即經過原型模式建立的對象,不單單與原型對象具備相同的結構,還與原型對象具備相同的值。函數

3) 根據對象克隆深度層次的不一樣,有淺度克隆與深度克隆。性能

2.2 應用場景

1) 在建立對象的時候,咱們不僅是但願被建立的對象繼承其父類的基本結構,還但願繼承原型對象的數據。this

2) 但願對目標對象的修改不影響既有的原型對象(深度克隆的時候能夠徹底互不影響)。spa

3) 隱藏克隆操做的細節。不少時候,對對象自己的克隆須要涉及到類自己的數據細節。.net

 

3、實現方式

咱們以克隆羊爲例,實現類須要實現 Cloneable 接口,同時要重寫從 Object 類中繼承的 clone 方法:設計

public class Sheep implements Cloneable{

private String name;

private Date birthday;

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Date getBirthday() {

return birthday;

}

public void setBirthday(Date birthday) {

this.birthday = birthday;

}

@Override

protected Object clone() throws CloneNotSupportedException {

return super.clone();

}

}

 

public class Client {

public static void main(String[] args) throws CloneNotSupportedException {

Date date = new Date(1365215478956L);

Sheep sheep = new Sheep();

sheep.setName("多利");

sheep.setBirthday(date);

Sheep clone = (Sheep) sheep.clone();

System.out.println("克隆羊名字:" + clone.getName());

System.out.println("克隆羊生日:" + clone.getBirthday());

}

}

 

客戶端:code

結果:

克隆羊名字:多利

克隆羊生日:Sat Apr 06 10:31:18 CST 2013


不過,上邊的克隆方式屬於淺度克隆,當對象中包含引用類型的屬性時,這樣淺克隆方式會存在一個問題。咱們試着修改 date 的值:當咱們須要更多的羊時,只需調用第一隻羊的 clone 方法便可,省去了給新建的對象設置屬性的操做。

public class Client {

public static void main(String[] args) throws CloneNotSupportedException {

Date date = new Date(1365215478956L);

Sheep sheep = new Sheep();

sheep.setName("多利");

sheep.setBirthday(date);

Sheep clone = (Sheep) sheep.clone();

System.out.println("克隆羊名字:" + clone.getName());

System.out.println("克隆羊生日:" + clone.getBirthday());

System.out.println("--------------------------------");

// 修改本體羊生日

date.setTime(3246584261356L);

System.out.println("本體羊生日:" + sheep.getBirthday());

System.out.println("克隆羊生日:" + clone.getBirthday());

}

}

 

克隆羊名字:多利

克隆羊生日:Sat Apr 06 10:31:18 CST 2013

--------------------------------

本體羊生日:Thu Nov 17 12:57:41 CST 2072

克隆羊生日:Thu Nov 17 12:57:41 CST 2072

 

結果打印:

咱們修改本體羊的日期,結果克隆羊的日期也發生變化。

由於淺度克隆只對基本數據類型的值進行備份,而引用類型的數據只是拷貝了指向對象的引用而已。其內存表現形式以下圖:

image

從圖中可知,兩隻羊共用一個日期對象。所以,當咱們修改日期時,兩隻羊的日期也發生變化。

爲了解決這一個問題,咱們可使用深度克隆方式,有 2 種方式能夠實現深度克隆。

方式一:在 clone 方法中將全部引用類型數據進行克隆。

實體類:

@Override

protected Object clone() throws CloneNotSupportedException {

Object obj = super.clone();

Sheep sheep = (Sheep) obj;

// 克隆時間

sheep.birthday = (Date) this.birthday.clone();

return sheep;

}


在運行客戶端代碼時,結果以下:在實體類的 clone 方法中,將引用類型的數據一塊兒克隆。

克隆羊名字:多利

克隆羊生日:Sat Apr 06 10:31:18 CST 2013

--------------------------------

本體羊生日:Thu Nov 17 12:57:41 CST 2072

克隆羊生日:Sat Apr 06 10:31:18 CST 2013


由於全部序列化方式,所以實體必須實現 Serializable 接口。方式二:使用序列化和反序列化進行克隆

實體類:

public class Sheep implements Cloneable,Serializable{

...

}

 

public class Client {

public static void main(String[] args) throws CloneNotSupportedException,ClassNotFoundException, IOException {

Date date = new Date(1365215478956L);

Sheep sheep = new Sheep();

sheep.setName("多利");

sheep.setBirthday(date);

//經過序列化和反序列化進行克隆

ByteArrayOutputStream bos = new ByteArrayOutputStream();

ObjectOutputStream oos = new ObjectOutputStream(bos);

oos.writeObject(sheep);

ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());

ObjectInputStream ois = new ObjectInputStream(bis);

Sheep clone = (Sheep) ois.readObject();

System.out.println("克隆羊名字:" + clone.getName());

System.out.println("克隆羊生日:" + clone.getBirthday());

System.out.println("--------------------------------");

// 修改本體羊生日

date.setTime(3246584261356L);

System.out.println("本體羊生日:" + sheep.getBirthday());

System.out.println("克隆羊生日:" + clone.getBirthday());

}

}

 

客戶端:

打印結果與方式一的結果一致。

深度克隆的內存表現形式以下圖:

image

4、性能比較

使用原型模式建立對象並不會調用類的構造方法。所以,在構造函數調用長且須要重複建立該類的實例的狀況下,使用原型模式建立對象的優點就體現出來了。

實體類:

public class Sheep implements Cloneable{

private String name;

private Date birthday;

public Sheep() {

try {

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

public String getName() {

return name;

}

public void setName(String name) {

this.name = name;

}

public Date getBirthday() {

return birthday;

}

public void setBirthday(Date birthday) {

this.birthday = birthday;

}

@Override

protected Object clone() throws CloneNotSupportedException {

return super.clone();

}

}


客戶端:延遲構造器調用的時間。

public class Client {

public static void main(String[] args) throws CloneNotSupportedException {

Sheep sheep = new Sheep();

int num = 1000;

long t1 = System.currentTimeMillis();

for (int i = 0; i < num; i++) {

Sheep tmp = new Sheep();

}

System.out.println("new 方式耗時:" + (System.currentTimeMillis() - t1) + "ms");

long t2 = System.currentTimeMillis();

for (int i = 0; i < num; i++) {

Sheep tmp = (Sheep) sheep.clone();

}

System.out.println("克隆方式耗時:" + (System.currentTimeMillis() - t2) + "ms");

}

}

 

new 方式耗時:10298ms

克隆方式耗時:1ms

 

結果:

相關文章
相關標籤/搜索