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> 逃避構造函數的約束。

使用原型模式複製對象不會調用類的構造方法。因爲對象的複製是通過調用Object類的clone方法來完成的,它直接在內存中複製數據,因此不會調用到類的構造方法。不但構造方法中的代碼不會執行,甚至連訪問權限都對原型模式無效。

四: 代碼演示

<淺拷貝> 

創建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()方法,較爲簡單



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章