設計模式-原型模式

原型模式(Prototype).就是對象的引用指向另外一個對象即 Object newObject = oldObject;java

這種作法是至關於newObject仍是指向oldObject的地址,也就是說,兩者其實是同樣的,將來也是同樣的,隨便對哪一個對象進行更改,兩者都會保持一致,由於能夠把它們看作兩個相同的「指針」;另一種常見的作法是,從新建立一個對象,用new來實例化,這樣就建立了另一個對象,即向內存中再寫入了一個對象,雖然內容同樣,但地址不同,可是這種作法費力,若是對象比較複雜的話。ide

原型模式在這種需求下就誕生了,咱們知道Object乃一切對象的父類(超類),而且Object有一個原生的clone方法,可是該方法的調用必需要求類實現了Cloneable接口,雖然Cloneable接口只是一個擺設,裏面空空蕩蕩,姑且就當Cloneable接口是clone方法實現的一個標誌吧!咱們能夠建立一個類實現Cloneable便可,在覆寫clone方法便可完成該類的克隆了。測試

原型模式的代碼實現

下面寫一個Dog類,該類實現了Cloneable接口,而且覆寫了超類的clone方法,裏面使用了super.clone,表示調用超類的原生代碼便可。注意,Dog類有一個非基本數據類型的變量eye,下面會介紹這個點。this

淺複製

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.Serializable;

public class Dog implements Cloneable,Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -2050795770781171788L;
	
	private String name;
	
	Eye eye;
	
	public Dog(Eye eye) {
		this.eye=eye;
	}

	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name=name;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		Dog dog;
		dog=(Dog) super.clone();
		return dog;
	}    
}
class Eye implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = -2723012171722328322L;
	String name;
	public Eye(String name) {
		this.name=name;
	}
}

原型模式中的複製方法

在上面的原型模式代碼中,咱們覆寫了clone方法,下面咱們來進行一個測試:spa

測試代碼一:

package com.prototype;

/**
 * @author zzw922cn
 *
 */
public class Test1 {

	public static void main(String[] args) throws CloneNotSupportedException {
		Dog dog = new Dog(new Eye("紅眼睛"));
		dog.setName("狗一");
		
		Dog object1 = (Dog) dog.clone();
		
		object1.eye.name="綠眼睛";
		object1.setName("狗二");
		
		System.out.println(dog.eye.name);
		System.out.println(object1.eye.name);
		
		System.out.println(dog.getName());
		System.out.println(object1.getName());
		
		System.out.println(object1.equals(dog));
		System.out.println(object1==dog);
		System.out.println(object1.getClass().equals(dog.getClass()));
	}
}

 

 

在上面的代碼中能夠看到,object1是dog的克隆對象,當咱們克隆完成之後,再對object1進行調用相關設置,改變其Eye類型的變量以及String類型的變量,會發生什麼呢?dog是否會發生變化呢?而且object1與dog是否同樣呢(equals和==)?它們是否屬於一樣的類呢?最後一個問題是毋庸置疑的,確定是同一個類。prototype

運行結果指針

綠眼睛
綠眼睛
狗一
狗二
false
false
true

 

 

從運行結果中能夠看到,在object1修改了eye對象之後,dog的eye對象的name也自動由紅眼睛變爲綠眼睛,可是object1修改了String類型的name對象後,dog卻保持原有的name對象。這兩者有什麼聯繫或區別嗎?聯繫是String和Eye都是非基本數據類型,Java的八大基本數據類型有char,byte,int,short,long,float,double,boolean。區別是既然同屬非基本數據類型,可是一個跟隨克隆對象變化而變化,另一個卻保持不變,這是很奇怪的。由於它是String類型,String是一個例外。所以,總結一下clone方法,咱們得知克隆對象的基本數據類型字段是原有對象字段的複製,可是非基本類型(String除外)並無複製,而是對原有對象的非基本類型的一個引用罷了,這種狀況正如博文一開始中的newObject與oldObject二者的關係。而String類型則是一個例外,它是對原有對象的一個複製,並不是指向原有的String對象的地址。code

這種clone通常稱爲淺複製,即並無徹底複製!對象

測試代碼二

下面來進行一次深複製,即徹底複製。我使用流的方式來進行深度複製,即徹底拷貝。接口

深複製

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.Serializable;

public class Dog implements Cloneable,Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -2050795770781171788L;
	
	private String name;
	
	Eye eye;
	
	public Dog(Eye eye) {
		this.eye=eye;
	}

	public String getName() {
		return name;
	}
	
	public void setName(String name) {
		this.name=name;
	}
	
	@Override
	protected Object clone() throws CloneNotSupportedException {
		Dog dog;
		dog=(Dog) super.clone();
		return dog;
	}
	
	/* 深複製 */  
    public Object deepClone() throws IOException, ClassNotFoundException {  
  
        /* 寫入當前對象的二進制流 */  
        ByteArrayOutputStream bos = new ByteArrayOutputStream();  
        ObjectOutputStream oos = new ObjectOutputStream(bos);  
        oos.writeObject(this);  
  
        /* 讀出二進制流產生的新對象 */  
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());  
        ObjectInputStream ois = new ObjectInputStream(bis);  
        return ois.readObject();  
    } 
    
}
class Eye implements Serializable{
	/**
	 * 
	 */
	private static final long serialVersionUID = -2723012171722328322L;
	String name;
	public Eye(String name) {
		this.name=name;
	}
}

 

 

接着寫一個相似的測試代碼

package com.prototype;

import java.io.IOException;

public class Test2 {
/**
 * equal強調內容是否相同
 * =強調地址是否相同
 * @param args
 * @throws CloneNotSupportedException
 * @throws IOException 
 * @throws ClassNotFoundException 
 */
	public static void main(String[] args) throws CloneNotSupportedException, ClassNotFoundException, IOException {
		Dog dog = new Dog(new Eye("紅眼睛"));
		dog.setName("狗一");
		
		System.out.println("-----------------深複製--------------");
		Dog object2 = (Dog) dog.deepClone();
		object2.eye.name="綠眼睛";
		object2.setName("狗二");
		
		System.out.println(dog.eye.name);
		System.out.println(object2.eye.name);
		
		System.out.println(dog.getName());
		System.out.println(object2.getName());
		
		System.out.println(object2.equals(dog));
		System.out.println(object2==dog);
		System.out.println(object2.getClass().equals(dog.getClass()));
		
		
	}
}

 

 

運行測試結果:

-----------------深複製--------------
紅眼睛
綠眼睛
狗一
狗二
false
false
true

 

 

咱們看到深度複製,兩者便「分道揚鑣」了,除了複製之初二者是同樣的以外,後續的任何變化都不會對彼此產生任何影響了。這就是深複製。

equals與==的區別

前面咱們看到克隆對象與原始對象的equals和==都返回false,不管是淺複製仍是深複製。那麼equals和==究竟是什麼關係呢?

對於基本數據類型,==比較的是它們的值。而對於非基本類型的對象,==比較是它們在內存中的地址,如以前的oldObject與newObject兩者的地址相同;而equals方法,若是它沒有被子類覆寫,它最原始的也是比較對象在內存中的地址,若是被子類覆寫了,就很差說了。例如在String類型中,equals方法比較的是字符串的「表面值」,它並非比較對象在內存中的地址,而==比較的是兩個字符串在內存中的地址是否同樣。例如String str1="Java",String str2=new String("Java");那麼兩者的關係是str1!=str2,str1.equals(str2)=true。緣由是str1存在於字符串常量池中,str2存在於Java堆中,兩者地址固然不一樣;可是兩者的表面值是同樣的,都是Java,所以equals方法返回true。

相關文章
相關標籤/搜索