天天看點

Java原型模式(prototype)

  prototype模式也就是原型模式,是javaGOF23種設計模式中的一種,我們在學習spring的時候在bean标簽的學習中碰到過,是以本文來給大家介紹下原型模式

原型模式

  在java中我們知道通過new關鍵字建立的對象是非常繁瑣的(類加載判斷,記憶體配置設定,初始化等),在我們需要大量對象的情況下,原型模式就是我們可以考慮實作的方式。

  原型模式我們也稱為克隆模式,即一個某個對象為原型克隆出來一個一模一樣的對象,該對象的屬性和原型對象一模一樣。而且對于原型對象沒有任何影響。原型模式的克隆方式有兩種:淺克隆和深度克隆

原型模式 說明
淺克隆

隻是拷貝本對象,其對象内部的數組、引用對象等都不拷貝,

還是指向原生對象的内部元素位址

深度克隆 深複制把要複制的對象所引用的對象都複制了一遍

淺克隆

被複制對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用仍然指向原來的對象。換言之,淺複制僅僅複制所考慮的對象,而不複制它所引用的對象。 Object類提供的方法clone隻是拷貝本對象 , 其對象内部的數組、引用對象等都不拷貝,還是指向原生對象的内部元素位址

實作

被克隆的對象必須Cloneable,Serializable這兩個接口

原型類

package com.dpb.prototype;

import java.io.Serializable;
import java.util.Date;

/**
 * 原型類:被克隆的類型
 * @author dengp
 *
 */
public class User implements Cloneable,Serializable{
  
  private String name;
  
  private Date birth;
  
  private int age;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Date getBirth() {
    return birth;
  }

  public void setBirth(Date birth) {
    this.birth = birth;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }
  
  /**
   * 實作克隆的方法
   */
  public Object clone() throws CloneNotSupportedException{
    return super.clone();
  }
}      

測試類

public static void main(String[] args) throws CloneNotSupportedException {
  Date date =  new Date(1231231231231l);
  User user = new User();
  user.setName("波波烤鴨");
  user.setAge(18);
  user.setBirth(date);
  System.out.println("----輸出原型對象的屬性------");
  System.out.println(user);
  System.out.println(user.getName());
  System.out.println(user.getBirth());
  // 克隆對象
  User user1 =(User) user.clone();
  // 修改原型對象中的屬性
  date.setTime(123231231231l);
  System.out.println(user.getBirth());
  
  // 修改參數
  user1.setName("dpb");
  System.out.println("-------克隆對象的屬性-----");
  System.out.println(user1);
  System.out.println(user1.getName());
  System.out.println(user1.getBirth());
}      

輸出結果

com.dpb.prototype.User@15db9742
波波烤鴨
Tue Jan 06 16:40:31 CST 2009 # 1
Tue Nov 27 14:53:51 CST 1973 # 2
-------克隆對象的屬性-----
com.dpb.prototype.User@5c647e05
dpb
Tue Nov 27 14:53:51 CST 1973 # 和2的結果一樣 說明兩個對象的Date的引用是同一個      

淺克隆的問題:雖然産生了兩個完全不同的對象,但是被複制的對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用都仍然指向原來的對象。

Java原型模式(prototype)

深度克隆

被複制對象的所有變量都含有與原來的對象相同的值,除去那些引用其他對象的變量。那些引用其他對象的變量将指向被複制過的新對象,而不再是原有的那些被引用的對象。換言之,深複制把要複制的對象所引用的對象都複制了一遍。

實作的效果是:

Java原型模式(prototype)

深度克隆(deep clone)有兩種實作方式,第一種是在淺克隆的基礎上實作,第二種是通過序列化和反序列化實作,我們分别來介紹

第一種方式

在淺克隆的基礎上實作

原型類:

package com.dpb.prototype;

import java.io.Serializable;
import java.util.Date;

/**
 * 原型類:被克隆的類型
 * 深度克隆測試
 * @author dengp
 *
 */
public class User2 implements Cloneable,Serializable{
  
  private String name;
  
  private Date birth;
  
  private int age;

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public Date getBirth() {
    return birth;
  }

  public void setBirth(Date birth) {
    this.birth = birth;
  }

  public int getAge() {
    return age;
  }

  public void setAge(int age) {
    this.age = age;
  }
  
  /**
   * 實作克隆的方法
   * 深度克隆(deep clone)
   */
  public Object clone() throws CloneNotSupportedException{
    Object object = super.clone();
    // 實作深度克隆(deep clone)
    User2 user = (User2)object;
    user.birth = (Date) this.birth.clone();
    return object;
  }
}      

測試代碼

public static void main(String[] args) throws CloneNotSupportedException {
  Date date =  new Date(1231231231231l);
  User2 user = new User2();
  user.setName("波波烤鴨");
  user.setAge(18);
  user.setBirth(date);
  System.out.println("----輸出原型對象的屬性------");
  System.out.println(user);
  System.out.println(user.getName());
  System.out.println(user.getBirth());
  // 克隆對象
  User2 user1 =(User2) user.clone();
  // 修改原型對象中的屬性
  date.setTime(123231231231l);
  System.out.println(user.getBirth());
  
  // 修改參數
  user1.setName("dpb");
  System.out.println("-------克隆對象的屬性-----");
  System.out.println(user1);
  System.out.println(user1.getName());
  System.out.println(user1.getBirth());
}      

輸出結果:

com.dpb.prototype.User2@15db9742
波波烤鴨
Tue Jan 06 16:40:31 CST 2009 1
Tue Nov 27 14:53:51 CST 1973 2
-------克隆對象的屬性-----
com.dpb.prototype.User2@5c647e05
dpb
Tue Jan 06 16:40:31 CST 2009      

我們發現克隆的對象的屬性并沒有随着我們對Date的修改而改變,說明克隆對象的Date屬性和原型對象的Date屬性引用的不是同一個對象,實作的深度複制。

第二種方式:序列化和反序列化

名稱 說明
序列化 把對象轉換為位元組序列的過程。
反序列化 把位元組序列恢複為對象的過程。
public static void main(String[] args) throws CloneNotSupportedException, Exception {
  Date date =  new Date(1231231231231l);
  User user = new User();
  user.setName("波波烤鴨");
  user.setAge(18);
  user.setBirth(date);
  System.out.println("-----原型對象的屬性------");
  System.out.println(user);
  System.out.println(user.getName());
  System.out.println(user.getBirth());
  
  //使用序列化和反序列化實作深複制
  ByteArrayOutputStream bos = new ByteArrayOutputStream();
  ObjectOutputStream    oos = new ObjectOutputStream(bos);
  oos.writeObject(user);
  byte[] bytes = bos.toByteArray();
  
  ByteArrayInputStream  bis = new ByteArrayInputStream(bytes);
  ObjectInputStream   ois = new ObjectInputStream(bis);
  
  //克隆好的對象!
  User user1 = (User) ois.readObject();   
  
  // 修改原型對象的值
  date.setTime(221321321321321l);
  System.out.println(user.getBirth());
  
  System.out.println("------克隆對象的屬性-------");
  System.out.println(user1);
  System.out.println(user1.getName());
  System.out.println(user1.getBirth());
}      

輸出結果

-----原型對象的屬性------
com.dpb.prototype.User@15db9742
波波烤鴨
Tue Jan 06 16:40:31 CST 2009
Sat May 24 16:48:41 CST 8983
------克隆對象的屬性-------
com.dpb.prototype.User@7cca494b
波波烤鴨
Tue Jan 06 16:40:31 CST 2009      

實作了和第一種實作方式相同的效果~實作了深度克隆

原型模式和直接new對象方式的比較

當我們需要大量的同一類型對象的時候可以使用原型模式,下面是兩種方式的性能對比:

用兩種方式同時生成10個對象

/**
 * 測試普通new方式建立對象和clone方式建立對象的效率差異!
 * 如果需要短時間建立大量對象,并且new的過程比較耗時。則可以考慮使用原型模式!
 * @author 波波烤鴨
 *
 */
public class Client4 {
  
  public static void testNew(int size){
    long start = System.currentTimeMillis();
    for(int i=0;i<size;i++){
      User t = new User();
    }
    long end = System.currentTimeMillis();
    System.out.println("new的方式建立耗時:"+(end-start));
  }
  
  public static void testClone(int size) throws CloneNotSupportedException{
    long start = System.currentTimeMillis();
    User t = new User();
    for(int i=0;i<size;i++){
      User temp = (User) t.clone();
    }
    long end = System.currentTimeMillis();
    System.out.println("clone的方式建立耗時:"+(end-start));
  }
  
  
  public static void main(String[] args) throws Exception { 
    testNew(10);
    testClone(10);
  }
}


class User implements Cloneable {  //使用者
  public User() {
    try {
      Thread.sleep(10);  //模拟建立對象耗時的過程!
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
  
  @Override
  protected Object clone() throws CloneNotSupportedException {
    Object obj = super.clone();  //直接調用object對象的clone()方法!
    return obj;
  }
}      

輸出結果:

new的方式建立耗時:108
clone的方式建立耗時:11      

用兩種方式同時生成1000個對象

/**
 * 測試普通new方式建立對象和clone方式建立對象的效率差異!
 * 如果需要短時間建立大量對象,并且new的過程比較耗時。則可以考慮使用原型模式!
 * @author 波波烤鴨
 *
 */
public class Client4 {
  
  public static void testNew(int size){
    long start = System.currentTimeMillis();
    for(int i=0;i<size;i++){
      User t = new User();
    }
    long end = System.currentTimeMillis();
    System.out.println("new的方式建立耗時:"+(end-start));
  }
  
  public static void testClone(int size) throws CloneNotSupportedException{
    long start = System.currentTimeMillis();
    User t = new User();
    for(int i=0;i<size;i++){
      User temp = (User) t.clone();
    }
    long end = System.currentTimeMillis();
    System.out.println("clone的方式建立耗時:"+(end-start));
  }
  
  
  public static void main(String[] args) throws Exception { 
    testNew(1000);
    testClone(1000);
  }
}


class User implements Cloneable {  //使用者
  public User() {
    try {
      Thread.sleep(10);  //模拟建立對象耗時的過程!
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }
  
  @Override
  protected Object clone() throws CloneNotSupportedException {
    Object obj = super.clone();  //直接調用object對象的clone()方法!
    return obj;
  }
}      

輸出結果

new的方式建立耗時:10836
clone的方式建立耗時:10      

開發中的應用場景

繼續閱讀