原型模式(Prototype Pattern)是用于创建重复的对象,同时又能保证性能。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
作者的话
写这篇文章前 作者自己也是看了好多篇文章才准备去写,好多人并没有说出来原型模式好处 究竟好在哪里,只是说效率高,或者是省事,在正式开始本文章前,先拿一段代码运行举例说明原型模式最直观的好处。
举例
我们这里举例 创建一千个特别复杂的对象,假设创建一个对象 用时10ms,通过一段代码去验证我们自己手动创建和使用原型模式创建的时间差,来证明原型模式的高效率。
/***
* 要构建的类
* @author CG_544
* /
public class Student implements Cloneable {
public Student() {
try {
// 模拟创建对象耗时10毫秒
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
protected Object clone() throws CloneNotSupportedException {
Object object = super.clone();
return object;
}
}
/***
* 测试类
*/
public class Test {
public static void newStu(int count) {
long start = System.currentTimeMillis();
for (int i = 0; i < count; i++) {
Student stu = new Student();
}
long end = System.currentTimeMillis();
System.out.println("通过new的方式创建对象耗时:" + (end - start));
}
public static void cloneStu(int count) throws CloneNotSupportedException {
long start = System.currentTimeMillis();
Student stu1 = new Student();
for (int i = 0; i < count; i++) {
Student stu2 = (Student) stu1.clone();
}
long end = System.currentTimeMillis();
System.out.println("通过clone的方式创建对象耗时:" + (end - start));
}
public static void main(String[] args) throws CloneNotSupportedException {
//测试原型模式在创建对象很耗时的情况下和在短时间需要创建大量的对象的情况下比new对象的情况下效率高
newStu(1000);
cloneStu(1000);
}
}
运行结果
显而易见的差距 产生这种差距的原因是什么呢?
因为clone方法 其实就是内存中拷贝了一份新的数据,使用二进制流的方式实现复制对象,不会走构造函数去创造对象,所以就会比较节约时间。
Java中如何实现原型模式
Java中的数据类型
1、基本数据类型的特点:直接存储在栈中的数据
2、引用数据类型的特点:存储的是该对象在栈中引用,真实的对象存放在堆内存里
(大概就是下面图片的意思)
深拷贝和浅拷贝是基于引用数据类型才引出的,因为存储地址不同,所以产生了深浅拷贝的情况
浅拷贝
浅拷贝指得是在克隆对象过程中,遇到了引用数据类型,直接在栈中,新建一个内存地址,指向原有对象的堆,并不会在堆中复制一份对象数据,所以浅拷贝会出现一种问题,我在克隆出来的对象中,修改引用类型的数据,原始对象的属性状态,也会被同步修改掉。
实现浅拷贝需要以下几步
1. 类实现Cloneable接口
2. 重写clone方法 直接调用父类的即可
3. 调用clone方法 复制对象即可
在这里解释一下为什么clone属于Object类的方法,还需要实现接口,并且需要重写clone方法
如上图所示,如果不去实现Cloneable接口,那样不可克隆,并且会抛出异常,可以把Cloneable当作一个标识,只有实现了这个接口才代表开启了克隆功能。
由于这个方法并不是在本地实现的,并且是受保护的方法,需要在子类进行重写,否则无法调用。
具体样例
@Override
protected Object clone() throws CloneNotSupportedException {
Object object = super.clone();
return object;
}
深拷贝
深拷贝 顾名思义,复制所有属性,修改新的对象属性,并不会影响原有对象。
深拷贝有两种方法,两种方法各有各自的好处,按需使用,当然名字都是我自己按照自己理解去命名的哈。
套娃拷贝法
第一种就不用具体的代码去实现了,我在这里就讲一下思路。
引用类型 (这里指的是某些复杂对象 比如说Student 有一个属性 Grade,Grade中包含GradeId和GradeName) 这种引用类型数据,其实Grade里面的属性, 也都是基本数据类型,所以我只要克隆Student类的同时,向下再克隆一层,将Grade也实现克隆一下即可。 如果还有更复杂的,就继续向下克隆即可。
这种方法的优劣势
- 优势:代码简洁,只需要一层一层的克隆就可以实现了
- 劣势:不一定要克隆多少层,如果比较复杂,即克隆层数比较不明确
序列化和反序列化法
核心要点:以流的方式 对一个对象进行序列化操作 并且再进行一次反序列化 即可得到深拷贝的对象
注意点:必须实现Serializable接口,并定义序列号
核心代码
//字节数组输出流在内存中创建一个字节数组缓冲区,所有发送到输出流的数据保存在该字节数组缓冲区中
ByteArrayOutputStream output=new ByteArrayOutputStream();
//创建对象输出流
ObjectOutputStream oo=new ObjectOutputStream(baos);
//将对象写入内存
oo.writeObject(this);
ByteArrayInputStream input=new ByteArrayInputStream(output.toByteArray());
//创建对象输入流
ObjectInputStream oi=new ObjectInputStream(input);
//将对象从内存的输出流中读回对象,完成反序列化模式下的深克隆操作
s=(Object) oi.readObject();
oi.close();
oo.close();
全面发展,一专多能!!!