1、描写
原型模式是通过1个原型对象来标明要创建的对象的类型,然后用复制这个原型对象的方法来拷贝创建更多的同类型对象。例如我们在程序的动态运行进程中有了1个对象,这个对象中包括了1系列的有效数据,我们此时需要1个和该对象完全相同的新对象,并且在拷贝以后,新旧对象之间没有任何联系,对任何1个对象的更改都不影响另外一个对象。
在java中所有类都默许继承自java.lang.Object类,在这个Object类中有1个clone()方法,该方法将返回Object对象的1个拷贝。
我们让需要被拷贝的类实现 Cloneable
接口,该接口用来唆使Object.clone()方法可以合法地对该类实例进行按字段复制。如果在没有实现Cloneable
接口的实例上调用 Object 的 clone 方法,则会致使抛出
CloneNotSupportedException
异常。
注意:Cloneable接口是1个标记接口,该接口中没有任何别的方法,只是作为标记来标明这个类可以合法的使用Object类的clone()方法来产生该实例的1个副本。
除Cloneable接口是标记接口以外还有Serializable接口(用于类启用其序列化功能,未实现此接口的类将没法使其任何状态序列化或反序列化)、RandomAccess接口(List
实现中所使用的标记接口,用来表明其支持快速(通常是固定时间)随机访问,从而在将其利用到随机或连续访问列表时能提供良好的性能)、Remote接口(Remote
接口用于标识其方法可以从非本地虚拟机上调用的接口。任何远程对象都必须直接或间接实现此接口。只有在“远程接口(扩大java.rmi.Remote
的接口)中指定的这些方法才可远程使用,实现类可以实现任意数量的远程接口,并且可以扩大其他远程实现类。RMI
提供1些远程对象实现可以扩大的有用类,这些类便于远程对象创建)。
2、原型设计模式的优缺点
优点:在原型模式中,可以动态地添加产品类,而且不会对整天结构产生影响,只是复制了1个对象而已。
缺点:由于原型模式要让每一个类实现Cloneable
接口,并重写Object类中的clone()方法,而且原型模式在实现深拷贝的时候需要补充更多的代码,这无疑增加了1定的代码量。
3、源代码
3.1 浅拷贝:如果待拷贝的对象中存在对象类型和援用类型,那末只拷贝对象和援用类型的地址,而是真正拷贝对象和援用中的数据。
package tong.day5_1.dogCase;
import java.util.ArrayList;
/**
* DogClone实现了Cloneable接口,重写clone()方法,调用父类的clone()拷贝1个对象并返回,Dog类并没有clone()方法,这是浅拷贝的模式。
* 浅拷贝:基本数据类型确切另外拷贝了1个副本,但是对对象类型和援用类型则只拷贝对象或援用的地址,
* 致使拷贝的对象中的对象类型的援用指向同1个对象,只要有1个修改了,就会影响另外一个对象中的数据。
* @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{
//基本数据类型拷贝时会直接拷贝数据值的1个副本,也就是说拷贝后产生的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()返回1个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()拷贝1个对象,并显式调用dog对象和arrayList对象的拷贝方法返回该类对象,这样拷贝产生的对象和原来的对象就互不干扰,相互独立。
package tong.day5_1.deepClone;
import java.util.ArrayList;
/**
* DogClone实现了Cloneable接口,重写clone()方法,调用父类的clone()拷贝1个对象,并显式调用dog对象和arrayList对象的拷贝方法返回该类对象,这是深拷贝的模式。
* 深拷贝:基本数据类型确切另外拷贝了1个副本,但是对对象类型和援用类型则默许的是只拷贝对象或援用的地址,
* 如果想要使这两种类型也拷贝对象和援用中的数据,那末就需要在重写的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 {
// 基本数据类型拷贝时会直接拷贝数据值的1个副本,也就是说拷贝后产生的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()返回1个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()返回1个dog对象的副本
dogClone.dog = (Dog) dog.clone();
//显式调用arrayList对象的clone()返回1个arrayList对象的副本
dogClone.arrayList = (ArrayList<String>) arrayList.clone();
return dogClone;
}
}
运行结果: