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