以前就纠结过一个问题,什么是基本类型数据,什么是引用类型数据,它们有什么区别,看了原型模式后,也算是知道了它们的一点区别了。
调用端:
WordDocument originalWd = new WordDocument(); originalWd.setText("这是原始文档"); originalWd.addImage("原始图片1"); originalWd.addImage("原始图片2"); originalWd.addImage("原始图片3"); originalWd.showDocument(); WordDocument copyWd = originalWd.clone(); copyWd.showDocument(); copyWd.setText("这是原始文档克隆出来的克隆文档"); copyWd.showDocument(); originalWd.showDocument();clone()方法并不是Cloneable接口中的,而是Object中的,Cloneable是一个标识接口,它表明这个对象是可拷贝的,如果没有实现Cloneable接口而调用clone()方法,则会抛出异常。克隆文档是通过original.clone()创建的,所以它们内容一样,而且克隆文档修改了text(注意,是text)后原始文档也没有改变,这就保证了原始文档的安全性。特别的是,通过clone()方法克隆的对象不会执行构造方法,所以如果在构造方法中有一些特殊操作的情况下,要注意构造方法不会执行的问题。
简单实现的原型模式只是一个浅拷贝,也称为影子拷贝,它并不是将原始文档的所有字段都重新构造了一份,而是克隆文档的字段引用原始文档的字段,实际上是两个文档的对象都指向了同一个地址。这也就是说,修改其中一个文档的基本数据类型的变量时,另一个文档不会受影响,但是如果修改其中一个文档的引用数据类型的变量时,另一个文档就会受到影响了。用代码试试看,修改一下原来的代码(其实也就只是加入了一行代码),结果就不一样了。
WordDocument originalWd = new WordDocument(); originalWd.setText("这是原始文档"); originalWd.addImage("原始图片1"); originalWd.addImage("原始图片2"); originalWd.addImage("原始图片3"); originalWd.showDocument(); WordDocument copyWd = originalWd.clone(); copyWd.showDocument(); copyWd.setText("这是原始文档克隆出来的克隆文档"); copyWd.addImage("克隆图片1"); copyWd.showDocument(); originalWd.showDocument(); 可以看到原始文档中的文本没有变,但是图片却多了一张。因为String是基本数据类型,List是引用数据类型,在克隆文档中增加了一张图片,由于克隆文档并不是重新构造一个iamges对象,而是和原始文档指向的同一个引用,所以原始文档也被修改了。这很显然不是我们希望看到的,如何解决这个问题,方法就是采用深拷贝,即在拷贝对象(引用数据类型)时,对于引用型的字段也要采取拷贝而不是单纯的引用的形式。 修改一下clone()方法。 @Override protected WordDocument clone() { try { WordDocument wordDocument = (WordDocument) super.clone(); wordDocument.text = this.text; // wordDocument.images = this.images; wordDocument.images = (ArrayList<String>) this.images.clone(); return wordDocument; } catch (CloneNotSupportedException e) { e.printStackTrace(); } return null; } 可以看到修改克隆文档中的引用数据类型的变量并不会影响到原始文档了。 原型模式是非常简单的一个模式,需要注意的问题就是浅拷贝和深拷贝的问题,一般为了减少失误,都尽量采用深拷贝。
Demo下载