java语言实现创建型设计模式—原型模式(Prototype)

一、描述

原型模式是通过一个原型对象来标明要创建的对象的类型,然后用复制这个原型对象的方法来拷贝创建更多的同类型对象。例如我们在程序的动态运行过程中有了一个对象,这个对象中包含了一系列的有效数据,我们此时需要一个和该对象完全相同的新对象,并且在拷贝之后,新旧对象之间没有任何联系,对任何一个对象的更改都不影响另一个对象。

在java中所有类都默认继承自java.lang.Object类,在这个Object类中有一个clone()方法,该方法将返回Object对象的一个拷贝。

我们让需要被拷贝的类实现 Cloneable 接口,该接口用来指示Object.clone()方法可以合法地对该类实例进行按字段复制。如果在没有实现Cloneable 接口的实例上调用 Object 的 clone 方法,则会导致抛出 CloneNotSupportedException 异常。


注意Cloneable接口是一个标记接口,该接口中没有任何别的方法,只是作为标记来标明这个类可以合法的使用Object类的clone()方法来产生该实例的一个副本。

除了Cloneable接口是标记接口之外还有Serializable接口(用于类启用其序列化功能,未实现此接口的类将无法使其任何状态序列化或反序列化)、RandomAccess接口List 实现中所使用的标记接口,用来表明其支持快速(通常是固定时间)随机访问,从而在将其应用到随机或连续访问列表时能提供良好的性能)、Remote接口Remote 接口用于标识其方法可以从非本地虚拟机上调用的接口。任何远程对象都必须直接或间接实现此接口。只有在“远程接口(扩展java.rmi.Remote 的接口)中指定的这些方法才可远程使用,实现类可以实现任意数量的远程接口,并且可以扩展其他远程实现类。RMI 提供一些远程对象实现可以扩展的有用类,这些类便于远程对象创建)。


二、原型设计模式的优缺点

优点:在原型模式中,可以动态地添加产品类,而且不会对整天结构产生影响,只是复制了一个对象而已。

缺点:由于原型模式要让每个类实现Cloneable 接口,并重写Object类中的clone()方法,而且原型模式在实现深拷贝的时候需要补充更多的代码,这无疑增加了一定的代码量。


三、源代码

3.1 浅拷贝:如果待拷贝的对象中存在对象类型和引用类型,那么只拷贝对象和引用类型的地址,而是真正拷贝对象和引用中的数据。

package tong.day5_1.dogCase;
import java.util.ArrayList;
/**
 * DogClone实现了Cloneable接口,重写clone()方法,调用父类的clone()拷贝一个对象并返回,Dog类并没有clone()方法,这是浅拷贝的模式。
 * 浅拷贝:基本数据类型确实另外拷贝了一个副本,但是对于对象类型和引用类型则只拷贝对象或引用的地址,
 * 导致拷贝的对象中的对象类型的引用指向同一个对象,只要有一个修改了,就会影响另一个对象中的数据。
 * @author tong
 *
 */
public class ShallowClone {
	public static void main(String[] args) { 
		//拷贝之前数据的值
		DogClone dogClone = new DogClone();		
		System.out.println("原来的dogClone.basicCount="+dogClone.basicCount);
		System.out.println("原来的dogClone.dog="+dogClone.dog);
		System.out.println("原来的dogClone.arraylist="+dogClone.arrayList);
		DogClone dogClone2 = (DogClone) dogClone.clone();
		System.out.println("-----------------------");
		//对拷贝的对象中的对象类型和引用类型的数据进行变更,则会影响另一个对象的数据;基本类型是进行值拷贝所以产生另一个副本,对原数据不会有影响
		dogClone2.basicCount = 2;
		Dog dog = dogClone2.dog;
		dog.changeCount();
		dogClone2.arrayList.add("java");
		//原对象中的基本数据类型的值不变
		System.out.println("后来的dogClone.basicCount="+dogClone.basicCount);
		System.out.println("后来的dogClone.dog="+dogClone.dog);
		System.out.println("后来的dogClone.arrayList="+dogClone.arrayList);
		System.out.println("dogClone2.basicCount="+dogClone2.basicCount);
		System.out.println("dogClone2.dog="+dogClone2.dog);
		System.out.println("dogClone2.arrayList="+dogClone2.arrayList);
	}
}

class Dog{
	public int legCount;
	public Dog(int legCount) {
		this.legCount = legCount;
	}
	
	public void changeCount() {
		this.legCount +=5;
	}
	//重写Dog类的toString()方法,在输出dog对象时调用该方法
	@Override
	public String toString() {
		
		return Integer.toString(legCount);
	}
}

//DogClone实现了Cloneable接口,只要重写Object中的clone()即可根据自己的需要拷贝对象
class DogClone implements Cloneable{
	//基本数据类型拷贝时会直接拷贝数据值的一个副本,也就是说拷贝后产生的legCount数值和原来被拷贝的值没有任何关系
	public int basicCount;
	//Dog对象类型,在使用浅拷贝的时候,拷贝的是栈中对象的地址,而不是对象地址所指向的堆内存的真实数据
	public Dog dog = new Dog(5);
	//ArrayList引用类型,在浅拷贝时,只拷贝引用的地址,而不是拷贝引用所指向的堆内存中的字符串
	public ArrayList<String> arrayList = new ArrayList<String>();
	public DogClone() {
		basicCount = 4;
		arrayList.add("hello");
		arrayList.add("world");
	}
	//重写了clone()返回一个DogClone类的副本对象
	@Override
	protected Object clone(){
		DogClone dogClone = null;
		try {
			dogClone =  (DogClone) super.clone();
		} catch (CloneNotSupportedException e) {	
			e.printStackTrace();
		}
		return dogClone;
	}
}
运行结果:

技术分享


3.2 深拷贝:DogClone实现了Cloneable接口,重写clone()方法,调用父类的clone()拷贝一个对象,并显式调用dog对象和arrayList对象的拷贝方法返回该类对象,这样拷贝产生的对象和原来的对象就互不干扰,相互独立。

package tong.day5_1.deepClone;
import java.util.ArrayList;

/**
 * DogClone实现了Cloneable接口,重写clone()方法,调用父类的clone()拷贝一个对象,并显式调用dog对象和arrayList对象的拷贝方法返回该类对象,这是深拷贝的模式。
 * 深拷贝:基本数据类型确实另外拷贝了一个副本,但是对于对象类型和引用类型则默认的是只拷贝对象或引用的地址,
 * 如果想要使这两种类型也拷贝对象和引用中的数据,那么就需要在重写的clone()方法中显式地调用该对象或者引用类型的clone()方法。
 * 所以我们需要让Dog类实现Cloneable接口使其具有拷贝能力,ArrayList间接继承了Object接口,本身就有clone()方法。
 * @author tong
 *
 */
public class DeepClone {
	public static void main(String[] args) {
		// 拷贝之前数据的值
		DogClone dogClone = new DogClone();
		System.out.println("原来的dogClone.basicCount=" + dogClone.basicCount);
		System.out.println("原来的dogClone.dog=" + dogClone.dog);
		System.out.println("原来的dogClone.arraylist=" + dogClone.arrayList);
		DogClone dogClone2 = (DogClone) dogClone.clone();
		System.out.println("-----------------------");
		// 这里进行的是深拷贝,对拷贝的对象中的对象类型和引用类型的数据进行变更,不会影响另一个对象的数据;基本类型是进行值拷贝所以产生另一个副本,对原数据也不会有影响
		dogClone2.basicCount = 2;
		Dog dog = dogClone2.dog;
		dog.changeCount();
		dogClone2.arrayList.add("java");
		// 原对象中所有数据的值都不变
		System.out.println("后来的dogClone.basicCount=" + dogClone.basicCount);
		System.out.println("后来的dogClone.dog=" + dogClone.dog);
		System.out.println("后来的dogClone.arrayList=" + dogClone.arrayList);
		System.out.println("dogClone2.basicCount=" + dogClone2.basicCount);
		System.out.println("dogClone2.dog=" + dogClone2.dog);
		System.out.println("dogClone2.arrayList=" + dogClone2.arrayList);
	}
}

//为了实现深拷贝,我们让Dog类实现Cloneable接口并重写该接口中的clone()方法
class Dog implements Cloneable {
	public int legCount;

	public Dog(int legCount) {
		this.legCount = legCount;
	}

	public void changeCount() {
		this.legCount += 5;
	}
	// 重写Dog类的toString()方法,在输出dog对象时调用该方法
	@Override
	public String toString() {

		return Integer.toString(legCount);
	}

	//重写clone()方法,使其dog对象具有自我复制的能力
	@Override
	protected Object clone() {
		Dog dog = null;
		try {
			dog = (Dog) super.clone();
		} catch (CloneNotSupportedException e) {
			e.printStackTrace();
		}
		return dog;
	}

}

////为了进行DogClone对象的拷贝,我们让DogClone类实现Cloneable接口并重写重写Object中的clone()方法
class DogClone implements Cloneable {
	// 基本数据类型拷贝时会直接拷贝数据值的一个副本,也就是说拷贝后产生的legCount数值和原来被拷贝的值没有任何关系
	public int basicCount;
	// Dog对象类型,在使用浅拷贝的时候,拷贝的是栈中对象的地址,而不是对象地址所指向的堆内存的真实数据
	public Dog dog = new Dog(5);
	// ArrayList引用类型,在浅拷贝时,只拷贝引用的地址,而不是拷贝引用所指向的堆内存中的字符串
	public ArrayList<String> arrayList = new ArrayList<String>();

	public DogClone() {
		basicCount = 4;
		arrayList.add("hello");
		arrayList.add("world");
	}
	//重写了clone()返回一个DogClone类的副本对象
	@Override
	protected Object clone() {
		DogClone dogClone = null;
		try {
			dogClone = (DogClone) super.clone();
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		//显式调用dog对象的clone()返回一个dog对象的副本
		dogClone.dog = (Dog) dog.clone();
		//显式调用arrayList对象的clone()返回一个arrayList对象的副本
		dogClone.arrayList = (ArrayList<String>) arrayList.clone();
		return dogClone;
	}
}
运行结果:

技术分享



郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。