原型模式的目的:解决创建重复对象的问题。
原型模式的原理:Java中Object类是所有类的根类,Object类提供了一个clone()方法,该方法可以将一个Java对象复制。但是需要实现clone的Java类必须实现Cloneable接口,该接口表示该类能够且具有复制的能力。
-
传统方式
我们知道如果有一个对象A a,要想获取一个和a相同的对象,需要新建一个A b = new A(); 并且将a中的内容逐一传给b。而不能直接用A b = a; 因为在java中这样只是引用传递,并没有真正复制一个新的对象出来。public class Demo { static class Person { int id; int age; String sex; public Person(int id, int age, String sex) { super(); this.id = id; this.age = age; this.sex = sex; } @Override public String toString() { return "Person [id=" + id + ", age=" + age + ", sex=" + sex + "]"; } } public static void main(String[] args) { Person person1 = new Person(1, 27, "male"); Person person2 = person1; // 错误的拷贝方式,只是引用传递 Person person3 = new Person(person1.id, person1.age, person1.sex); System.out.println("person1: " + person1.toString() + " hashCode:" + person1.hashCode()); System.out.println("person2: " + person2.toString() + " hashCode:" + person2.hashCode()); System.out.println("person3: " + person3.toString() + " hashCode:" + person3.hashCode()); } }
运行结果为:
person1: Person [id=1, age=27, sex=male] hashCode:118352462 person2: Person [id=1, age=27, sex=male] hashCode:118352462 person3: Person [id=1, age=27, sex=male] hashCode:1550089733
我们可以看到这种方式,需要代码获取原对象的属性,才能创建新的对象。如果原对象比较复杂,那么这个过程将会痛苦而漫长。如果原对象类的属性有所修改,那么采用这种方式拷贝的代码也都需要修改。这就需要我们的原型模式。
-
原型模式(默认为浅拷贝)
Person类实现Cloneable接口,并重载clone()方法。在复制对象时,只需要调用该对象的clone()方法即可。public class PrototypeDemo { static class Person implements Cloneable { int id; int age; String sex; public Person(int id, int age, String sex) { super(); this.id = id; this.age = age; this.sex = sex; } @Override public String toString() { return "Person [id=" + id + ", age=" + age + ", sex=" + sex + "]"; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } public static void main(String[] args) { Person person1 = new Person(1, 27, "male"); System.out.println("person1: " + person1.toString() + " hashCode:" + person1.hashCode()); try { Person person2 = (Person) person1.clone(); // 对象拷贝 System.out.println("person2: " + person2.toString() + " hashCode:" + person2.hashCode()); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } }
运行结果:
person1: Person [id=1, age=27, sex=male] hashCode:118352462 person2: Person [id=1, age=27, sex=male] hashCode:1550089733
该方法即为原型模式,对象拷贝方式简单,并且无论原对象的类属性是否变化,只需调用clone()方法,即可完成对象拷贝。但是该方式默认为浅拷贝,对于数据类型是基本类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新的对象;对于数据类型是引用的成员变量,比如成员变量是其它的类对象,那么浅拷贝只能进行引用传递,也就是把该成员变量的引用值复制给新的对象,但两者实际上还是指向同一个实例。(关于浅拷贝和深拷贝,如有需求,需另找资料进行学习)。如果需要深拷贝,也就是对于数据类型是引用的成员变量,也都把引用的实例复制给新的对象,那么需要第3步实现。
-
深拷贝
为验证原型模式为浅拷贝,修改原型模式代码,在Person中添加引用数据类型的成员变量friend。public class PrototypeDemo { static class Person implements Cloneable { int id; int age; String sex; Person friend; public Person(int id, int age, String sex) { super(); this.id = id; this.age = age; this.sex = sex; } @Override public String toString() { return "Person [id=" + id + ", age=" + age + ", sex=" + sex + ", friend.hashCode=" + friend.hashCode() + "]"; } @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } public static void main(String[] args) { Person person1 = new Person(1, 27, "male"); person1.friend = new Person(2, 27, "female"); System.out.println("person1: " + person1.toString() + " hashCode:" + person1.hashCode()); try { Person person2 = (Person) person1.clone(); System.out.println("person2: " + person2.toString() + " hashCode:" + person2.hashCode()); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } }
运行结果:
person1: Person [id=1, age=27, sex=male, friend.hashCode=118352462] hashCode:1550089733 person2: Person [id=1, age=27, sex=male, friend.hashCode=118352462] hashCode:865113938
我们可以看到person1和person2的hashCode不同,确实是两个对象,但是他们的friend.hashCode相同,说明在person1的拷贝过程中,person1的friend只是将其引用给了person2的friend,所以他俩还是指向了一个对象。深拷贝就是为了让person1.friend复制一个新的对象给person2.friend。
实现深拷贝有两种方式,第一种方式是在clone()方法中添加代码实现类中对象的拷贝,第二种方式通过序列化和反序列化来实现深拷贝。我理解第二种方式不能叫做原型模式吧?下面通过示例说明- 重写clone()方法
这里只粘贴部分代码,因为我不舍得重新写代码,friend.clone()调用的还是这个函数,所以有一个递归,如果这里的friend还有friend,就会接着往下走。运行结果如下:@Override protected Object clone() throws CloneNotSupportedException { Person personClone = (Person)super.clone(); if (friend != null) { personClone.friend = (Person)friend.clone(); } return personClone; }
搞定!person1: Person [id=1, age=27, sex=male, friend.hashCode=118352462] hashCode:1550089733 person2: Person [id=1, age=27, sex=male, friend.hashCode=865113938] hashCode:1442407170
- 通过序列化和反序列化来实现
运行结果:import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; public class DeepCloneDemo { static class Person implements Serializable { /** * generated serial version ID */ private static final long serialVersionUID = 9079128548395795470L; int id; int age; String sex; Person friend; public Person(int id, int age, String sex) { super(); this.id = id; this.age = age; this.sex = sex; } @Override public String toString() { return "Person [id=" + id + ", age=" + age + ", sex=" + sex + ", friend.hashCode=" + friend.hashCode() + "]"; } public Object deepClone() throws IOException, ClassNotFoundException { // 序列化操作 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(this); oos.flush(); // 反序列化操作 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); Person personClone = (Person)ois.readObject(); return personClone; } } public static void main(String[] args) { Person person1 = new Person(1, 27, "male"); person1.friend = new Person(2, 27, "female"); System.out.println("person1: " + person1.toString() + " hashCode:" + person1.hashCode()); try { Person person2 = (Person) person1.deepClone(); System.out.println("person2: " + person2.toString() + " hashCode:" + person2.hashCode()); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }
搞定,收工!对于序列化的了解不足,就不先在这多解释了。person1: Person [id=1, age=27, sex=male, friend.hashCode=118352462] hashCode:1550089733 person2: Person [id=1, age=27, sex=male, friend.hashCode=1831932724] hashCode:1747585824
- 重写clone()方法