1.模式描述
用原型实例指定创建对象的种类,并且通过拷贝来创建新的对象。他分为浅克隆和深克隆(也可以叫拷贝)
1.浅拷贝
什么是浅拷贝?浅拷贝后的结果是对象的内存地址变化了(对象的引用发生了变化),可对象中包含的对象内存则没有变化。只能克隆对象本身,对于依附于对象的对象则不予克隆,只对其地址克隆(这句很关键.)
浅克隆需要注意的地方:
(1)被克隆的对象需要实现需要实现Cloneable;
(2)要重写clone() 方法;
(3)对于依附于被克隆对象的对象,只克隆其地址。一旦更改都会改变。
2.深拷贝
上面说到对象的浅拷贝,在拷贝中我们对可变对象的操作有一些棘手。而深拷贝则可以解决这个问题。深拷贝的实现要依赖与对象的持久化操作。更多关于对象持久化的内容可参见本人另一篇博客:《网络爬虫:基于对象持久化实现爬虫现场快速还原》。深克隆利用了IO流进行存储,然后进行读取,不仅对克隆对象进行克隆
对象持久化是说把一个对象写到文件中(或是向网络,或是向进程之间进行传输),当我们需要拷贝一个对象时,先把此对象固化到文件,再从文件中读取对象。这样一个过程就完成了对象的深拷贝,在博客《网络爬虫:基于对象持久化实现爬虫现场快速还原》中我也有详细地说明,这里就不再赘述了。
深克隆,无论原型对象的成员变量是值类型还是引用类型,都将复制一份给克隆对象。
深克隆需要注意的地方:
(1)利用IO流进行存储,然后进行读取;
(2)需要实现可序列化接口Serializable。
优点:
(1)当创建一个结构复杂的对象时,原型模式可以简化其创建过程,通过复制已有的实例来获得相同的对象;
(2)简化创建结构,不需要专门的工厂存在,仅需要实现克隆方法即可。
缺点:
(1)必须实现Cloneable接口;
(2)逃避构造函数的约束;
(3)克隆方法在类的内部,加重类的职责,当需要对已有类进行改造时需要修改原型类。
浅拷贝和深拷贝的区别;通俗一点讲,:复制地址内存地址的叫浅拷贝,复制东西的叫深拷贝 .
拷贝的对象和原来对象地址值相同(都指向堆中同一个对象地址),就是浅拷贝, 如果拷贝的对象和原对象,地址值不同(内存堆中,不同的对象地址),就是深拷贝!(理解错误)
2.模式作用
可以一定程度上解耦,消费者和对象的构造过程隔离开,对象如何构造与消费者完全无关。
可以一定程度提升效率,复杂对象的构造往往需要较长的时间(中间可能会进行复杂运算或者数据库交互),clone消耗的资源一般情况下会少很多。
可以一定程度增加代码的封装性,避免复杂的构造过程。
等等。
3.适用场景
需要创建一个复杂的、构造耗时很多的对象,且已有一个同类对象的时候。
消费者不关心对象构造过程的时候。
等等。
例如:工作流实例的重建、复杂数据实体的复制
4.模式要素
该类要支持克隆。该类要实现Cloneable接口。
已有该类的一个实例。该实例作为原型进行clone。
该类的克隆方法应为深克隆。该类中除了8中基本类型(以及他们的封装类)、String(其实是浅clone,但是了解一下String就会发现其实结果和深clone一致,这里我们认为就是深clone)之外的属性均需要进行深clone;或者采用流化clone方式进行深clone。
本文借鉴于;http://blog.youkuaiyun.com/zhangjg_blog/article/details/18369201 和 http://blog.youkuaiyun.com/chenliguan/article/details/69855738 和https://www.cnblogs.com/chenxkang/p/6696228.html
4.1 优点
(1)原型模式是在内存中二进制流的拷贝,要比直接new一个对象性能好很多,特别是要在一个循环体内产生大量对象时,原型模式可能更好的体现其优点。
(2)还有一个重要的用途就是保护性拷贝,也就是对某个对象对外可能是只读的,为了防止外部对这个只读对象的修改,通常可以通过返回一个对象拷贝的形式实现只读的限制。
曾经有人进行过数据优化测试,采用原型模式和new 对象出来,测试结果如下;
原型模式构建对象和 new 对象效率比较
采用原型模式创建对象200次耗时:0
采用new创建对象200次耗时:2057
所以,通过原型模式创建对象,可以大大提高创建的效率,直接克隆,避免了重新执行构造过程。原型模式和工厂模式搭配起来,是常用的使用方式。
4.2 缺点
(1)这既是它的优点也是缺点,直接在内存中拷贝,构造函数是不会执行的,在实际开发中应该注意这个潜在问题。优点是减少了约束,缺点也是减少了约束,需要大家在实际应用时考虑。
(2)通过实现Cloneable接口的原型模式在调用clone函数构造实例时并不一定比通过new操作速度快,只有当通过new构造对象较为耗时或者说成本较高时,通过clone方法才能够获得效率上的提升。
代码;
A.浅拷贝:
/**
* Created by dwb on 2018/3/12.
* describe1: 创建型设计模式之 原型设计模式
* describe2: 第一步,创建一个基本类,并实现Cloneable接口,重写Clone的方法
* email:wolfking0608@163.com
*/
public class Sender implements Cloneable {
private int age;//基本数据类型
private String name;//引用数据类型
public Sender(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Object clone() throws CloneNotSupportedException {
return (Sender)super.clone();
}
}
2.测试
/**
* Created by dwb on 2018/3/14.
* describe1:原型设计模式, 浅拷贝测试
* describe2:
* email:wolfking0608@163.com
*/
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Sender sender = new Sender(11, "king");
Sender cloneSender = (Sender) sender.clone();
String name = sender.getName();
String cloneName = cloneSender.getName();
if (name == cloneName) {//这里如果换成是两个对象进行对比,结果却是不相等,对象不是存储在堆中吗?为啥地址值不同呢?目前先放在这里.有知道的请留言
System.out.println("clone属于浅拷贝");
} else {
System.out.println("属于深拷贝");
}
}
}
运行结果;
clone属于浅拷贝
B.深拷贝
1.
/**
* Created by dwb on 2018/3/12.
* describe1: 创建型设计模式之 原型设计模式
* describe2: 第一步,创建一个基本类,并实现Cloneable接口,重写Clone的方法
* email:wolfking0608@163.com
*/
public class Sender implements Cloneable {
private int age;//基本数据类型
private String name;//引用数据类型
public Sender(int age, String name) {
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected Sender clone() throws CloneNotSupportedException {
Sender sender = (Sender) super.clone();
return sender;
}
}
2.
/**
* Created by dwb on 2018/3/14.
* describe1:
* describe2:
* email:wolfking0608@163.com
*/
public class Prototype implements Cloneable {
private int id;
private Sender mSender;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public Sender getSender() {
return mSender;
}
public void setSender(Sender sender) {
mSender = sender;
}
public Object clone() {
try {
boolean deep = true;
if (deep) {
/**
* 深复制,复制出了两个sender
* */
Prototype prototype = (Prototype) super.clone();
prototype.setSender((Sender) this.mSender.clone());
// 继续复制其他引用对象
return prototype;
}else{
/**
* 浅复制 ,是同一个sender
* */
return super.clone();
}
} catch (CloneNotSupportedException e) {
e.printStackTrace();
return null;
}
}
}
3.
/**
* Created by dwb on 2018/3/14.
* describe1:原型设计模式, 深拷贝测试
* describe2:
* email:wolfking0608@163.com
*/
public class Test {
public static void main(String[] args) throws CloneNotSupportedException {
Prototype p1 = new Prototype();
p1.setSender(new Sender(11, "king"));
p1.setId(1);
// 复制
Prototype p2 = (Prototype) p1.clone();
p2.setId(2);
System.out.println(p1 == p2 ? "p1和p2是浅克隆" : "p1和p2是深克隆");
System.out.println(p1.getSender()==p2.getSender()?"p1的sender和p2的sender地址一样":"p1的sender和p2的sender地址不同");
}
}
运行结果:
1. deep为true时;
2.deep为false时
最后安卓开发中,对于该原型模式,用到的概率非常非常小.所以你可以先了解下.....
本文深入探讨了浅拷贝和深拷贝的概念及其在Java中的实现方式,包括它们的优缺点及应用场景。通过实例演示了如何实现这两种拷贝,并展示了在实际编程中如何选择合适的拷贝方式。

被折叠的 条评论
为什么被折叠?



