java设计模式 - 原型(干货)

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

一、为什么有原型模式

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

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

 

二、原型设计模式写法

 原型设计模式实现:

public class Person implements Cloneable {

private String userName;

private String sex;

private int age;

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
protected Person clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
return person;
}

@Override
public String toString() {
return "Person{" +
"userName='" + userName + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
'}';
}
}
public static void main(String[] args) throws CloneNotSupportedException {
Person p = new Person();
p.setAge(19);
p.setUserName("小明");
p.setSex("男");
Person p1 = p.clone();

System.out.println(p.hashCode());
System.out.println(p1.hashCode());

p.setAge(12);

System.out.println(p);
System.out.println(p1);
}
执行结果

  java设计模式 - 原型(干货)_第1张图片

 

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

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

 1,浅拷贝

public class Person implements Cloneable {

private String userName;

private String sex;

private int age;

private Son son;

public Son getSon() {
return son;
}

public void setSon(Son son) {
this.son = son;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
protected Person clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
return person;
}

@Override
public String toString() {
return "Person{" +
"userName='" + userName + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", son=" + son +
'}';
}
}
public class Son {

private String sonName;

public String getSonName() {
return sonName;
}

public void setSonName(String sonName) {
this.sonName = sonName;
}

@Override
public String toString() {
return "Son{" +
"sonName='" + sonName + '\'' +
'}';
}
}
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Person p = new Person();
p.setAge(19);
p.setUserName("小明");
p.setSex("男");
Son son = new Son();
son.setSonName("小明的孩子");
p.setSon(son);
Person p1 = p.clone();

System.out.println(p.hashCode());
System.out.println(p1.hashCode());

p1.getSon().setSonName("隔壁老王的孩子");
p1.setAge(12);

System.out.println(p);
System.out.println(p1);
}
}
运行结果如下

  java设计模式 - 原型(干货)_第2张图片

 那么为什么能够出现这样的结果呢?

 因为clone方法并不能穿透到son,也就是p和p1公用了son对象(p1对象对son的clone只是copy了指针,并没有完全copy,也就是浅拷贝),所以p和p1同时输出的“隔壁老王的孩子”。

 java设计模式 - 原型(干货)_第3张图片

 

  如果我们希望实现完整的clone,该如何处理呢?我们需要深拷贝。

 2,深拷贝

  1)套娃式深拷贝

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

public class Person implements Cloneable {

private String userName;

private String sex;

private int age;

private Son son;

public Son getSon() {
return son;
}

public void setSon(Son son) {
this.son = son;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
protected Person clone() throws CloneNotSupportedException {
Person person = (Person) super.clone();
person.setSon(person.getSon().clone());
return person;
}

@Override
public String toString() {
return "Person{" +
"userName='" + userName + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", son=" + son +
'}';
}
}

public class Son implements Cloneable {

private String sonName;

public String getSonName() {
return sonName;
}

public void setSonName(String sonName) {
this.sonName = sonName;
}

@Override
public String toString() {
return "Son{" +
"sonName='" + sonName + '\'' +
'}';
}

@Override
protected Son clone() throws CloneNotSupportedException {
return (Son) super.clone();
}
}
我们再来看结果:

  java设计模式 - 原型(干货)_第4张图片

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

  2)序列化深拷贝

  首先改造Person

public class Person implements Serializable {

private String userName;

private String sex;

private int age;

private Son son;

public Son getSon() {
return son;
}

public void setSon(Son son) {
this.son = son;
}

public String getUserName() {
return userName;
}

public void setUserName(String userName) {
this.userName = userName;
}

public String getSex() {
return sex;
}

public void setSex(String sex) {
this.sex = sex;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Person{" +
"userName='" + userName + '\'' +
", sex='" + sex + '\'' +
", age=" + age +
", son=" + son +
'}';
}
}
接着,改造Son
public class Son implements Serializable {

private String sonName;

public String getSonName() {
return sonName;
}

public void setSonName(String sonName) {
this.sonName = sonName;
}

@Override
public String toString() {
return "Son{" +
"sonName='" + sonName + '\'' +
'}';
}

}
Main方法
public class Test {
public static void main(String[] args) throws CloneNotSupportedException, IOException, ClassNotFoundException {
Person p = new Person();
p.setAge(19);
p.setUserName("小明");
p.setSex("男");
Son son = new Son();
son.setSonName("小明的孩子");
p.setSon(son);

ByteArrayOutputStream bos=new ByteArrayOutputStream();
ObjectOutputStream oos=new ObjectOutputStream(bos);
oos.writeObject(p);

ByteArrayInputStream bis=new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois=new ObjectInputStream(bis);
Person p1= (Person) ois.readObject();


System.out.println(p.hashCode());
System.out.println(p1.hashCode());

p1.getSon().setSonName("隔壁老王的孩子");
p1.setAge(12);

System.out.println(p);
System.out.println(p1);
}
}
来看看输出结果:

  java设计模式 - 原型(干货)_第5张图片

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

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

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

 

你可能感兴趣的:(java设计模式 - 原型(干货))