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的引用是同一個
淺克隆的問題:雖然産生了兩個完全不同的對象,但是被複制的對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用都仍然指向原來的對象。
深度克隆
被複制對象的所有變量都含有與原來的對象相同的值,除去那些引用其他對象的變量。那些引用其他對象的變量将指向被複制過的新對象,而不再是原有的那些被引用的對象。換言之,深複制把要複制的對象所引用的對象都複制了一遍。
實作的效果是:
深度克隆(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