深度讲解23种设计模式,力争每种设计模式都刨析到底。废话不多说,开始第二种设计模式 - 原型。
一、为什么有原型模式
当一个类需要克隆的时候,我们总不希望new一个对象,然后逐个属性去设置值。 这个时候,我们亟需一种
高效的对象copy方法,原型设计模式应运而生。
二、原型设计模式写法
原型设计模式实现:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Person implements Cloneable { 2 3 private String userName; 4 5 private String sex; 6 7 private int age; 8 9 public String getUserName() { 10 return userName; 11 } 12 13 public void setUserName(String userName) { 14 this.userName = userName; 15 } 16 17 public String getSex() { 18 return sex; 19 } 20 21 public void setSex(String sex) { 22 this.sex = sex; 23 } 24 25 public int getAge() { 26 return age; 27 } 28 29 public void setAge(int age) { 30 this.age = age; 31 } 32 33 @Override 34 protected Person clone() throws CloneNotSupportedException { 35 Person person = (Person) super.clone(); 36 return person; 37 } 38 39 @Override 40 public String toString() { 41 return "Person{" + 42 "userName='" + userName + '\'' + 43 ", sex='" + sex + '\'' + 44 ", age=" + age + 45 '}'; 46 } 47 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public static void main(String[] args) throws CloneNotSupportedException { 2 Person p = new Person(); 3 p.setAge(19); 4 p.setUserName("小明"); 5 p.setSex("男"); 6 Person p1 = p.clone(); 7 8 System.out.println(p.hashCode()); 9 System.out.println(p1.hashCode()); 10 11 p.setAge(12); 12 13 System.out.println(p); 14 System.out.println(p1); 15 }
执行结果
原型实现,在Person类中,实现了Cloneable 接口,然后重写clone方法,即可。
一切看似都很美好,然而,如果在Person中增加一个不是基本类型,也不是String类型的属性时,情况会令你意外。
1,浅拷贝
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Person implements Cloneable { 2 3 private String userName; 4 5 private String sex; 6 7 private int age; 8 9 private Son son; 10 11 public Son getSon() { 12 return son; 13 } 14 15 public void setSon(Son son) { 16 this.son = son; 17 } 18 19 public String getUserName() { 20 return userName; 21 } 22 23 public void setUserName(String userName) { 24 this.userName = userName; 25 } 26 27 public String getSex() { 28 return sex; 29 } 30 31 public void setSex(String sex) { 32 this.sex = sex; 33 } 34 35 public int getAge() { 36 return age; 37 } 38 39 public void setAge(int age) { 40 this.age = age; 41 } 42 43 @Override 44 protected Person clone() throws CloneNotSupportedException { 45 Person person = (Person) super.clone(); 46 return person; 47 } 48 49 @Override 50 public String toString() { 51 return "Person{" + 52 "userName='" + userName + '\'' + 53 ", sex='" + sex + '\'' + 54 ", age=" + age + 55 ", son=" + son + 56 '}'; 57 } 58 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Son { 2 3 private String sonName; 4 5 public String getSonName() { 6 return sonName; 7 } 8 9 public void setSonName(String sonName) { 10 this.sonName = sonName; 11 } 12 13 @Override 14 public String toString() { 15 return "Son{" + 16 "sonName='" + sonName + '\'' + 17 '}'; 18 } 19 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Test { 2 public static void main(String[] args) throws CloneNotSupportedException { 3 Person p = new Person(); 4 p.setAge(19); 5 p.setUserName("小明"); 6 p.setSex("男"); 7 Son son = new Son(); 8 son.setSonName("小明的孩子"); 9 p.setSon(son); 10 Person p1 = p.clone(); 11 12 System.out.println(p.hashCode()); 13 System.out.println(p1.hashCode()); 14 15 p1.getSon().setSonName("隔壁老王的孩子"); 16 p1.setAge(12); 17 18 System.out.println(p); 19 System.out.println(p1); 20 } 21 }
运行结果如下
那么为什么能够出现这样的结果呢?
因为clone方法并不能穿透到son,也就是p和p1公用了son对象(p1对象对son的clone只是copy了指针,并没有完全copy,也就是浅拷贝),所以p和p1同时输出的“隔壁老王的孩子”。
如果我们希望实现完整的clone,该如何处理呢?我们需要深拷贝。
2,深拷贝
1)套娃式深拷贝
我们在浅拷贝的基础上修改,修改部分笔者用红色标识。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Person implements Cloneable { 2 3 private String userName; 4 5 private String sex; 6 7 private int age; 8 9 private Son son; 10 11 public Son getSon() { 12 return son; 13 } 14 15 public void setSon(Son son) { 16 this.son = son; 17 } 18 19 public String getUserName() { 20 return userName; 21 } 22 23 public void setUserName(String userName) { 24 this.userName = userName; 25 } 26 27 public String getSex() { 28 return sex; 29 } 30 31 public void setSex(String sex) { 32 this.sex = sex; 33 } 34 35 public int getAge() { 36 return age; 37 } 38 39 public void setAge(int age) { 40 this.age = age; 41 } 42 43 @Override 44 protected Person clone() throws CloneNotSupportedException { 45 Person person = (Person) super.clone(); 46 person.setSon(person.getSon().clone()); 47 return person; 48 } 49 50 @Override 51 public String toString() { 52 return "Person{" + 53 "userName='" + userName + '\'' + 54 ", sex='" + sex + '\'' + 55 ", age=" + age + 56 ", son=" + son + 57 '}'; 58 } 59 }
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Son implements Cloneable { 2 3 private String sonName; 4 5 public String getSonName() { 6 return sonName; 7 } 8 9 public void setSonName(String sonName) { 10 this.sonName = sonName; 11 } 12 13 @Override 14 public String toString() { 15 return "Son{" + 16 "sonName='" + sonName + '\'' + 17 '}'; 18 } 19 20 @Override 21 protected Son clone() throws CloneNotSupportedException { 22 return (Son) super.clone(); 23 } 24 }
我们再来看结果:
很显然,得到了我们期望的结果,像不像套娃?
2)序列化深拷贝
首先改造Person
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Person implements Serializable { 2 3 private String userName; 4 5 private String sex; 6 7 private int age; 8 9 private Son son; 10 11 public Son getSon() { 12 return son; 13 } 14 15 public void setSon(Son son) { 16 this.son = son; 17 } 18 19 public String getUserName() { 20 return userName; 21 } 22 23 public void setUserName(String userName) { 24 this.userName = userName; 25 } 26 27 public String getSex() { 28 return sex; 29 } 30 31 public void setSex(String sex) { 32 this.sex = sex; 33 } 34 35 public int getAge() { 36 return age; 37 } 38 39 public void setAge(int age) { 40 this.age = age; 41 } 42 43 @Override 44 public String toString() { 45 return "Person{" + 46 "userName='" + userName + '\'' + 47 ", sex='" + sex + '\'' + 48 ", age=" + age + 49 ", son=" + son + 50 '}'; 51 } 52 }
接着,改造Son
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Son implements Serializable { 2 3 private String sonName; 4 5 public String getSonName() { 6 return sonName; 7 } 8 9 public void setSonName(String sonName) { 10 this.sonName = sonName; 11 } 12 13 @Override 14 public String toString() { 15 return "Son{" + 16 "sonName='" + sonName + '\'' + 17 '}'; 18 } 19 20 }
Main方法
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Test { 2 public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException { 3 Person p = new Person(); 4 p.setAge(19); 5 p.setUserName("小明"); 6 p.setSex("男"); 7 Son son = new Son(); 8 son.setSonName("小明的孩子"); 9 p.setSon(son); 10 11 ByteArrayOutputStream bos=new ByteArrayOutputStream(); 12 ObjectOutputStream oos=new ObjectOutputStream(bos); 13 oos.writeObject(p); 14 15 ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray()); 16 ObjectInputStream ois=new ObjectInputStream(bis); 17 Person p1= (Person) ois.readObject(); 18 19 20 System.out.println(p.hashCode()); 21 System.out.println(p1.hashCode()); 22 23 p1.getSon().setSonName("隔壁老王的孩子"); 24 p1.setAge(12); 25 26 System.out.println(p); 27 System.out.println(p1); 28 } 29 }
来看看输出结果:
很显然,得到了我们期望的结果。
当然,可以把序列化过程封装到Person中。没有了套娃,又可以欢乐玩耍了。
总结:每一种拷贝都有自己的应用场景,要看具体的业务场景。