设计模式-原型模式

原型模式(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。

相关文章
相关标签/搜索