共计 7007 个字符,预计需要花费 18 分钟才能阅读完成。
写在后面
- 记录学习设计模式的笔记
- 进步对设计模式的灵活运用
学习地址
https://www.bilibili.com/vide…
https://www.bilibili.com/vide…
参考文章
http://c.biancheng.net/view/1…
我的项目源码
https://gitee.com/zhuang-kang/DesignPattern
6,原型模式
6.1 原形模式的定义和特点
原型(Prototype)模式的定义如下:用一个曾经创立的实例作为原型,通过复制该原型对象来创立一个和原型雷同或类似的新对象。在这里,原型实例指定了要创立的对象的品种。用这种形式创建对象十分高效,基本毋庸晓得对象创立的细节。例如,Windows 操作系统的装置通常较耗时,如果复制就快了很多。
原型模式的长处:
- Java 自带的原型模式基于内存二进制流的复制,在性能上比间接 new 一个对象更加低劣。
- 能够应用深克隆形式保留对象的状态,应用原型模式将对象复制一份,并将其状态保存起来,简化了创建对象的过程,以便在须要的时候应用(例如复原到历史某一状态),可辅助实现撤销操作。
原型模式的毛病:
- 须要为每一个类都配置一个 clone 办法
- clone 办法位于类的外部,当对已有类进行革新的时候,须要批改代码,违反了开闭准则。
- 当实现深克隆时,须要编写较为简单的代码,而且当对象之间存在多重嵌套援用时,为了实现深克隆,每一层对象对应的类都必须反对深克隆,实现起来会比拟麻烦。因而,深克隆、浅克隆须要使用切当。
6.2 原型模式的构造与实现
6.2.1 原形模式的构造
原型模式蕴含以下次要角色。
- 形象原型类:规定了具体原型对象必须实现的接口。
- 具体原型类:实现形象原型类的 clone() 办法,它是可被复制的对象。
- 拜访类:应用具体原型类中的 clone() 办法来复制新的对象。
6.2.2 代码实现
浅克隆:创立一个新对象,新对象的属性和原来对象完全相同,对于非根本类型属性,仍指向原有属性所指向的对象的内存地址。
深克隆:创立一个新对象,属性中援用的其余对象也会被克隆,不再指向原有对象地址。
6.2.2.1 浅拷贝
IdCard
package com.zhuang.prototype.shallowclone;
/**
* @Classname IdCard
* @Description 浅拷贝的示例
* @Date 2021/3/19 12:16
* @Created by dell
*/
public class IdCard {
private String id;
public IdCard(String id) {this.id = id;}
@Override
public String toString() {
return "IdCard{" +
"id=" + id +
'}';
}
}
Person
package com.zhuang.prototype.shallowclone;
/**
* @Classname Person
* @Description 浅拷贝的示例
* @Date 2021/3/19 12:17
* @Created by dell
*/
public class Person implements Cloneable {
private String name;
private int age;
private IdCard idCard;
public Person() {}
public Person(String name, int age, IdCard idCard) {
this.name = name;
this.age = age;
this.idCard = idCard;
}
public String getName() {return name;}
public void setName(String name) {this.name = name;}
public int getAge() {return age;}
public void setAge(int age) {this.age = age;}
public IdCard getIdCard() {return idCard;}
public void setIdCard(IdCard idCard) {this.idCard = idCard;}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", idCard=" + idCard + ", idCard.hashCode=" + idCard.hashCode() +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {return super.clone();
}
}
PersonTest
package com.zhuang.prototype.shallowclone;
/**
* @Classname PersonTest
* @Description 浅拷贝测试类
* @Date 2021/3/19 12:17
* @Created by dell
*/
public class PersonTest {public static void main(String[] args) throws Exception {Person person = new Person("张三", 20, new IdCard("10086"));
Person person1 = (Person) person.clone();
Person person2 = (Person) person.clone();
System.out.println(person);
System.out.println(person1);
System.out.println(person2);
}
}
咱们发现能够通过实现 implements Cloneable
来实现浅拷贝,根本变量是值传递克隆,而援用对象 IdCard
则是援用传递,这不合乎咱们面向对象思维,每一个 Person
应该都有一个独立的IdCard
,而不是共用一个,而要解决这种问题,咱们须要应用深克隆
6.2.2.2 深拷贝(第一种)
IdCard
package com.zhuang.prototype.deepclone.one;
/**
* @Classname IdCard
* @Description 深克隆的示例
* @Date 2021/3/19 12:33
* @Created by dell
*/
// 实现 Cloneable 接口
public class IdCard implements Cloneable {
private String id;
public IdCard(String id) {this.id = id;}
public String getId() {return id;}
public void setId(String id) {this.id = id;}
@Override
public String toString() {
return "IdCard{" +
"id='" + id + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {return super.clone();
}
}
Person
package com.zhuang.prototype.deepclone.one;
/**
* @Classname Person
* @Description 深克隆的示例
* @Date 2021/3/19 12:33
* @Created by dell
*/
public class Person implements Cloneable {
private String name;
private int age;
private IdCard idCard;
public Person(String name, int age, IdCard idCard) {
this.name = name;
this.age = age;
this.idCard = idCard;
}
public IdCard getIdCard() {return idCard;}
public void setIdCard(IdCard idCard) {this.idCard = idCard;}
@Override
public String toString() {
return "Person{" +
"personHashCode=" + this.hashCode() +
", name='" + name + '\'' +
", age=" + age +
", idCard=" + idCard +
", idCardHashCode=" + idCard.hashCode() +
'}';
}
// 深克隆须要本人手动实现,在对象援用中也要实现 clone 办法
@Override
protected Object clone() throws CloneNotSupportedException {
// 实现根本数据类型的拷贝
// 通过 new 关键字创立的对象是援用类型
Object person = super.clone();
// 对援用类型独自解决
Person p = (Person) person;
IdCard idCard = (IdCard) p.getIdCard().clone(); // 实现本人的克隆
p.setIdCard(idCard);
return p;
}
}
PersonTest
package com.zhuang.prototype.deepclone.one;
import java.io.Serializable;
/**
* @Classname PersonTest
* @Description 深克隆测试类
* @Date 2021/3/19 12:33
* @Created by dell
*/
public class PersonTest implements Serializable {public static void main(String[] args) throws Exception {Person person = new Person("张三", 20, new IdCard("10086"));
Person person1 = (Person) person.clone();
Person person2 = (Person) person.clone();
System.out.println(person);
System.out.println(person1);
System.out.println(person2);
}
}
应用这种深克隆的形式,完满的解决了当数据类型为援用类型时,只是拷贝原援用对象地址而不是一个全新的援用对象的援用,然而这种实现有一个很大的弊病,须要在每一个对象中都实现 clone 办法,如果类全是你本人写的,那天然没问题,实现一下就行了,不过有点麻烦。然而,如果你援用的是第三方的一个类,无奈批改源代码,这种形式,显然就无奈实现深克隆了
6.2.2.2 深拷贝(第二种)
IdCard
package com.zhuang.prototype.deepclone.two;
/**
* @Classname IdCard
* @Description 深克隆的示例 2
* @Date 2021/3/19 12:33
* @Created by dell
*/
// 实现 Serializable 接口
public class IdCard implements Serializable {
private String id;
public IdCard(String id) {this.id = id;}
public String getId() {return id;}
public void setId(String id) {this.id = id;}
@Override
public String toString() {
return "IdCard{" +
"id='" + id + '\'' +
'}';
}
@Override
protected Object clone() throws CloneNotSupportedException {return super.clone();
}
}
Person
package com.zhuang.prototype.deepclone.two;
import java.io.*;
/**
* @Classname Person
* @Description 深克隆的示例 2
* @Date 2021/3/19 12:33
* @Created by dell
*/
public class Person implements Serializable {
private String name;
private int age;
private IdCard idCard;
public Person(String name, int age, IdCard idCard) {
this.name = name;
this.age = age;
this.idCard = idCard;
}
public IdCard getIdCard() {return idCard;}
public void setIdCard(IdCard idCard) {this.idCard = idCard;}
@Override
public String toString() {
return "Person{" +
"personHashCode=" + this.hashCode() +
", name='" + name + '\'' +
", age=" + age +
", idCard=" + idCard +
", idCardHashCode=" + idCard.hashCode() +
'}';
}
// 序列化的形式
public Person deelClone() {
// 创立流对象
ByteArrayOutputStream bos = null;
ObjectOutputStream oos = null;
ByteArrayInputStream bis = null;
ObjectInputStream ois = null;
try {
// 序列化
bos = new ByteArrayOutputStream();
oos = new ObjectOutputStream(bos);
oos.writeObject(this);
// 反序列化
bis = new ByteArrayInputStream(bos.toByteArray());
ois = new ObjectInputStream(bis);
return (Person) ois.readObject();} catch (Exception e) {e.printStackTrace();
return null;
} finally {
try {ois.close();
bis.close();
oos.close();
bos.close();} catch (IOException e) {e.printStackTrace();
}
}
}
}
PersonTest
package com.zhuang.prototype.deepclone.two;
/**
* @Classname PersonTest
* @Description 深克隆测试类
* @Date 2021/3/19 12:33
* @Created by dell
*/
public class PersonTest {public static void main(String[] args) throws Exception {
// 创立一个对象
Person person = new Person("张三", 20, new IdCard("10086"));
// 克隆两个对象
Person person1 = (Person) person.deelClone();
Person person2 = (Person) person.deelClone();
System.out.println("深拷贝(第二种 实现序列化接口)");
// 打印三人信息
System.out.println(person);
System.out.println(person1);
System.out.println(person2);
}
}
这种形式咱们须要手动编写 deepClone 办法,应用 Java 流中的序列化与反序列化来实现深克隆,然而这种实现,须要在每一个类中都继承序列化 Serializable 接口,这种形式,如果你调用的是第三方类,也有可能第三方类上没有实现 Serializable 序列化接口,然而一般来说,大多都会实现,总的来说,这种比拟举荐应用,而且效率也高
6.3 原型模式的利用场景
- 对象之间雷同或类似,即只是个别的几个属性不同的时候。
- 创建对象老本较大,例如初始化工夫长,占用 CPU 太多,或者占用网络资源太多等,须要优化资源。
- 创立一个对象须要繁琐的数据筹备或拜访权限等,须要进步性能或者进步安全性。
- 零碎中大量应用该类对象,且各个调用者都须要给它的属性从新赋值。
- 在 Spring 中,原型模式利用的十分宽泛,例如 scope=’prototype’、JSON.parseObject() 等都是原型模式的具体利用。
6.4 原型模式的注意事项和细节
- 创立新的对象比较复杂时,能够利用原型模式简化对象的创立过程,同时也能提高效率
- 不必从新初始化对象,动静地取得对象运行时的状态
- 如果原始对象发生变化(减少或缩小属性),其余克隆对象也会产生相应的变动,无需批改代码
写在最初
- 如果我的文章对你有用,请给我点个👍,感激你😊!
- 有问题,欢送在评论区指出!💪