首页 > 技术文章 > java设计模式 - 原型(干货)

x-j-p 2020-02-28 23:33 原文

 深度讲解23种设计模式,力争每种设计模式都刨析到底。废话不多说,开始第二种设计模式 - 原型。

一、为什么有原型模式

 当一个类需要克隆的时候,我们总不希望new一个对象,然后逐个属性去设置值。 这个时候,我们亟需一种

高效的对象copy方法,原型设计模式应运而生。

 

二、原型设计模式写法

 原型设计模式实现:

 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 }
Person类
 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 }
main方法
执行结果

  

 

 原型实现,在Person类中,实现了Cloneable 接口,然后重写clone方法,即可。

 一切看似都很美好,然而,如果在Person中增加一个不是基本类型,也不是String类型的属性时,情况会令你意外。

 1,浅拷贝

 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 }
Person类
 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 }
Son类
 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)套娃式深拷贝

  我们在浅拷贝的基础上修改,修改部分笔者用红色标识。

 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 }
Person类

 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 }
Son类

我们再来看结果:

  

  很显然,得到了我们期望的结果,像不像套娃?

  2)序列化深拷贝

  首先改造Person

 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 }
Person类
接着,改造Son
 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 }
Son类
Main方法
 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 }
main方法

来看看输出结果:

  

 很显然,得到了我们期望的结果。

 当然,可以把序列化过程封装到Person中。没有了套娃,又可以欢乐玩耍了。

 总结:每一种拷贝都有自己的应用场景,要看具体的业务场景。

 

推荐阅读