Java設計模式 - 原型模式簡單介紹及其例項應用
一:原型模式的定義?
定義:用原型例項指定建立物件的種類,並且通過拷貝這些原型建立新的物件。
簡單地講:給定一個原型物件來指明所要建立的物件的型別,然後用拷貝這個原型物件的方法來創建出更多的同類型物件。
二:JAVA中原型模式的實現?
在JAVA裡,通過克隆(Clone())方法來實現原型模式。
任何類,要想支援克隆,必須實現一個介面 Cloneable,該介面中有clone()方法,可以在類中重寫自定義的克隆方法。
克隆的實現方法有三種:
(1)淺拷貝:淺拷貝是指在拷貝物件時,對於基本資料型別的變數會重新複製一份,而對於引用型別的變數只是對直接引用進行拷貝,沒有對直接引用指向的物件進行拷貝。
(2)深拷貝:深拷貝是指在拷貝物件時,不僅把基本資料型別的變數會重新複製一份,同時會對引用指向的物件進行拷貝。
(3)完全拷貝:在包括上面兩者共同點的基礎上把物件間接引用的物件也進行拷貝。這是最徹底的一種拷貝。通常先使物件序列化,實現Serializable介面 然後將物件寫進二進位制流裡 再從二進位制流裡讀出新物件。
克隆的特點:
(1)克隆的物件與原物件並不是同一個物件,分別佔用不同的堆空間 x.clone()!=x
(2)克隆的物件與原物件的型別一樣 x.clone().getClass()==x.clone().getClass()
(3)克隆物件不會呼叫建構函式
三: 原型模式的優點?
<1> 提高效能
使用原型模式建立物件比直接new一個物件在效能上要好的多,因為Object類的clone方法是一個本地方法,它直接操作記憶體中的二進位制流,特別是複製大物件時,效能的差別非常明顯。
<2>簡化物件的建立
因為以上優點,所以在需要重複地建立相似物件時可以考慮使用原型模式。比如需要在一個迴圈體內建立物件,假如物件建立過程比較複雜或者迴圈次數很多的話,使用原型模式不但以使系統的整體效能提高很多而且可以簡化建立過程,
<3> 逃避建構函式的約束。
使用原型模式複製物件不會呼叫類的構造方法。因為物件的複製是通過呼叫
四: 程式碼演示。
<淺拷貝>
建立Address 物件類 ,重寫clone()方法
package com.prototype;
public class Address implements Cloneable{
private String location;
public Address(String location){
this.location=location;
}
public String getLocation() {
return location;
}
public void setLocation(String location) {
this.location = location;
}
public Address clone(){
Address a=null;
try {
a=(Address)super.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return a;
}
}
測試類:
package com.prototype;
/**
* 淺拷貝測試類
*
* @author vean
*
*/
public class ShallowCopying {
public static void main(String[] args) {
Address address=new Address("北京"); //新建一個物件 ,這個物件用作被克隆的原型
Address cloneAddress= address.clone(); // 呼叫原型物件的克隆方法,克隆出一個物件
System.out.println("提供的原型城市名稱: "+address.getLocation());
System.out.println("克隆的的城市名稱: "+cloneAddress.getLocation());
System.out.println("兩個城市物件"+(address == cloneAddress?"相同":"不同")); //測試被可伶的物件與原物件是否是同一個物件
}
}
輸出結果:
<深拷貝>
建立Student類 ,為其新增地址address成員物件,重寫clone()方法
package com.prototype;
public class Student implements Cloneable{
private String name;
Address address;
public Student(String name, Address address) {
super();
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
public Student clone(){
Student s=null;
try {
s=(Student)super.clone();
s.address=(Address)address.clone();
} catch (CloneNotSupportedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return s;
}
}
測試類:
package com.prototype;
/**
* 深拷貝測試類
*
* @author vean
*
*/
public class DeepCoping {
public static void main(String[] args) {
Address address = new Address("北京");
Student s = new Student("張三", address);
Student sclone = (Student) s.clone();
System.out.println("學生姓名:" + s.getName() + " 克隆學生姓名:" + sclone.getName());
System.out.println("學生地址:" + s.address.getLocation() + " 克隆學生地址:" + sclone.address.getLocation());
System.out.println("兩個學生物件"+(s == sclone?"相同":"不同")); //測試被可伶的物件與原物件是否是同一個物件
System.out.println("修改被克隆學生姓名為李四,地址為四川,");
s.setName("李四");
s.address.setLocation("四川");
System.out.println("學生姓名:" + s.getName() + " 克隆學生姓名:" + sclone.getName());
System.out.println("學生地址:" + s.address.getLocation() + " 克隆學生地址:" + sclone.address.getLocation());
// 淺複製只複製值型別的變數和對物件的引用
// 深複製不僅複製值型別的變數,把原物件引用的物件也進行復制.
}
}
執行結果:
<完全拷貝>
新建Author類,實現 Serializable 介面 建立absoluteClone()方法 這裡要記住該物件引用的物件 Address類也需要實現 Serializable 介面
package com.prototype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.io.Serializable;
public class Author implements Serializable {
/**
*
*/
private static final long serialVersionUID = 2719717328965672807L;
private String name;
Address address;
public Author(String name, Address address) {
super();
this.name = name;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
// 使用序列化技術實現完全拷貝
public Author absoluteClone() throws IOException, ClassNotFoundException, OptionalDataException
{
// 將物件寫入流中
ByteArrayOutputStream bao = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bao);
oos.writeObject(this);
// 將物件從流中取出
ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
return (Author) ois.readObject();
}
}
測試類:
package com.prototype;
import java.io.IOException;
import java.io.OptionalDataException;
/**
* 完全拷貝測試類
*
* @author vean
*
*/
public class AbsoluteCopying {
public static void main(String[] args) throws OptionalDataException, ClassNotFoundException, IOException {
Address address = new Address("南京");
Author author = new Author("老王", address);
Author cloneAuthor = author.absoluteClone();
System.out.println("原作者作者姓名: " + author.getName() + " 住址: " + author.getAddress().getLocation());
System.out.println("克隆作者姓名: " + cloneAuthor.getName() + " 住址: " + cloneAuthor.getAddress().getLocation());
System.out.println("修改原作者姓名為 隔壁老王,住址為 北京");
author.setName("隔壁老王");
author.setAddress(new Address("北京"));
System.out.println("原作者作者姓名: " + author.getName() + " 住址: " + author.getAddress().getLocation());
System.out.println("克隆作者姓名: " + cloneAuthor.getName() + " 住址: " + cloneAuthor.getAddress().getLocation());
}
}
執行結果:
由此可見 完全拷貝將某對物件直接引用物件,包括引用物件引用的物件都重新複製,不需要實現Cloneable介面,重寫Clone()方法,較為簡單