设计模式(第五式:原型模式)
概念:
原型模式:使用原型实例指定要创建对象的类型,并通过复制此原型创建新的对象。这句话怎么这么绕呢?没办法英文翻译过来就这样,那是个什么意思呢?简单来说,就是我们定义一个类型,给这个类型一个实例化对象,然后就可以用这个对象去复制新的对象了。
原型模式一般来说分为两种:浅克隆模式和深克隆模式。之所以分为这两种模式的原因是如果当前类型的属性中包含了复杂类型,而Object中默认的clone方法不会帮我们复制复杂类型中的成员变量。其实这个挺好玩的,有些人可能会问为什么要克隆呢?直接new个对象不就好了么?或者直接赋值就有了啊? 直接new当然是可以的,但是有至少两个问题:1.如果原型类中有100个成员变量,而且这100个还都是复杂成员变量,那么你可能需要new的不是一下,而是有很多个;2.Object中的clone是底层通过二进制流的模式实现的,理论上性能要比new一个对象的性能高一些,理论上模型对象越复杂越能体现克隆的优越性。这就是还是要使用这种模式的原因。至于赋值的问题,赋值得到的不是一个新的对象,而是同一个对象的新的引用。看实现吧
实现:
作者类:
public class Author implements Serializable { private String name; private Integer age; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } }
浅克隆:
public class CloneBook implements Cloneable,Serializable { private String name; private Author author; public String getName() { return name; } public void setName(String name) { this.name = name; } public Author getAuthor() { return author; } public void setAuthor(Author author) { this.author = author; } @Override public CloneBook clone() { try { return (CloneBook) super.clone(); } catch (CloneNotSupportedException e) { System.out.println("没有实现Cloneable接口,就会跑改异常"); e.printStackTrace(); } return null; } }
深克隆:
public class DeepCloneBook implements Cloneable,Serializable { private String name; private Author author; public String getName() { return name; } public void setName(String name) { this.name = name; } public Author getAuthor() { return author; } public void setAuthor(Author author) { this.author = author; } @Override public DeepCloneBook clone() { try { ByteArrayOutputStream bao = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bao); oos.writeObject(this); //将对象从流中取出 ByteArrayInputStream bis = new ByteArrayInputStream(bao.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return (DeepCloneBook) ois.readObject(); } catch (IOException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } return null; } }
测试实现及结果
实现:
@Test public void CloneTest() { CloneBook cloneBook = new CloneBook(); Author author = new Author(); author.setName("mario"); author.setAge(18); cloneBook.setAuthor(author); cloneBook.setName("BookName"); CloneBook cloneBookClone = cloneBook.clone(); CloneBook cloneBookEvaluation = cloneBook; System.out.println("模版hashCode:"+ cloneBook.hashCode()); System.out.println("克隆hashCode:"+ cloneBookClone.hashCode()); System.out.println("赋值hashCode:"+ cloneBookEvaluation.hashCode()); System.out.println("模版Author.hashCode:"+ cloneBook.getAuthor().hashCode()); System.out.println("克隆Author.hashCode:"+ cloneBookClone.getAuthor().hashCode()); System.out.println("赋值Author.hashCode:"+ cloneBookEvaluation.getAuthor().hashCode()); } @Test public void DeepCloneTest() { DeepCloneBook cloneBook = new DeepCloneBook(); Author author = new Author(); author.setName("mario"); author.setAge(18); cloneBook.setAuthor(author); cloneBook.setName("BookName"); DeepCloneBook cloneBookClone = cloneBook.clone(); DeepCloneBook cloneBookEvaluation = cloneBook; System.out.println("模版hashCode:"+ cloneBook.hashCode()); System.out.println("克隆hashCode:"+ cloneBookClone.hashCode()); System.out.println("赋值hashCode:"+ cloneBookEvaluation.hashCode()); System.out.println("模版Author.hashCode:"+ cloneBook.getAuthor().hashCode()); System.out.println("克隆Author.hashCode:"+ cloneBookClone.getAuthor().hashCode()); System.out.println("赋值Author.hashCode:"+ cloneBookEvaluation.getAuthor().hashCode()); }
浅克隆结果: 模版hashCode:194706439 克隆hashCode:1392906938 赋值hashCode:194706439 模版Author.hashCode:708890004 克隆Author.hashCode:708890004 赋值Author.hashCode:708890004 深克隆结果: 模版hashCode:90205195 克隆hashCode:1684792003 赋值hashCode:90205195 模版Author.hashCode:21257599 克隆Author.hashCode:2038148563 赋值Author.hashCode:21257599
分析:有没有觉得这个好简单,但是还是有一些问题需要注意
1.虽然Cloneable接口中是空的,没有任何方法,但如果不实现就会抛“CloneNotSupportedException”异常,这种接口被称为标示接口,就是它啥都不干,但没它不行。
2.从测试结果中看的出来浅克隆的Author对象实际上是同一个,所以如果是浅克隆,我们在修改克隆对象的author属性的,模版对象的author对象也会被改变,这也是我们需要深克隆的原因。
3.深克隆时因为用到了流模式读取,所以必须实现Serializable接口,如果没有实习该接口,会抛“NotSerializableException”异常,并且模版类中的复杂类都要实现,这其中包括复杂类中的复杂类,即就是如果Author中还引用了复杂类,那么该类也需要实现Serializable接口,当然浅克隆是不需要的。
4.适用场景:
a.需要大量新对象,并且新对象创建复杂。
b.对象需要多个地方修改,并保留做对比。
...
经典框架中使用的:
hibernate、mybatis即成log4j等日志系统中对比前后修改量时会用到