文章目录
1. 定义
指原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象
调用者不需要知道任何创建细节,不调用构造函数
2. 适用场景
- 类初始化消耗资源较多
- new产生的一个对象需要非常繁琐的过程(数据准备,访问权限等)
- 构造函数比较复杂
- 循环体重生产大量对象时
- Spring中的scope就是使用的原型模式
3. 分类
- 浅克隆: 指拷贝对象时仅仅copy对象本身和对象中的基本变量,而不拷贝对象包含的引用指向的对象
- 深克隆: 不仅copy对象本身,而且copy对象包含的引用指向的所有对象
4. 浅克隆实现
4.1 克隆方法接口
package com.zhunongyun.toalibaba.designpatterns.prototype.simelp;
public interface Prototype {
Prototype clone();
}
4.2 待克隆类
package com.zhunongyun.toalibaba.designpatterns.prototype.simelp;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ConcretePrototypeA implements Prototype {
/**
* 年龄
*/
private int age;
/**
* 姓名
*/
private String name;
/**
* 兴趣爱好
*/
private List hobbies;
@Override
public Prototype clone() {
return new ConcretePrototypeA(this.age, this.name, this.hobbies);
}
}
4.3 测试类
package com.zhunongyun.toalibaba.designpatterns.prototype.simelp;
import java.util.ArrayList;
public class PrototypeTest {
public static void main(String[] args) {
ConcretePrototypeA concretePrototypeA = new ConcretePrototypeA(18, "test_name", new ArrayList());
ConcretePrototypeA copy = (ConcretePrototypeA) concretePrototypeA.clone();
System.out.println("原对象与克隆对象内存地址对比, 地址是否相同:"
+ (concretePrototypeA == copy));
System.out.println("原对象与克隆对象中引用类型内存地址对比, 地址是否相同:"
+ (concretePrototypeA.getHobbies() == copy.getHobbies()));
}
}
4.4 总结
- 浅克隆对于基本数据类型和String类型字段会重新申请内存复制数据,克隆对象会指向新的内存地址
- 浅克隆对于引用类型的字段不会重新申请内存,而是把字段的内存地址指向之前原对象字段的内存地址
5. 深克隆
定义一个孙悟空类, 拔一根毫毛吹出千万个猴子, 每个猴子都有属于自己的金箍棒
通过字节码实现深克隆
5.1 待克隆类
package com.zhunongyun.toalibaba.designpatterns.prototype.deep;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class JinGuBang implements Serializable {
private float height = 100;
private float wide = 10;
public void big() {
this.height *= 2;
this.wide *= 2;
}
public void small() {
this.height /= 2;
this.wide /= 2;
}
}
package com.zhunongyun.toalibaba.designpatterns.prototype.deep;
import lombok.Data;
import org.apache.commons.io.IOUtils;
import java.io.*;
import java.util.Date;
@Data
public class QiTianDaSheng implements Cloneable, Serializable {
private JinGuBang jinGuBang;
private int height;
private int weight;
private Date birthday;
public QiTianDaSheng() {
this.birthday = new Date();
this.jinGuBang = new JinGuBang();
}
@Override
protected Object clone() {
return this.deepClone();
}
public Object deepClone() {
ByteArrayOutputStream byteArrayOutputStream = null;
ObjectOutputStream objectOutputStream = null;
ByteArrayInputStream byteArrayInputStream = null;
ObjectInputStream objectInputStream = null;
// 内存中完成操作,对象读写,是通过字节码直接操作
// 序列化操作类似
try {
byteArrayOutputStream = new ByteArrayOutputStream();
objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
objectOutputStream.writeObject(this);
byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
objectInputStream = new ObjectInputStream(byteArrayInputStream);
// 完整的新的对象, new出来一个新的对象
QiTianDaSheng copy = (QiTianDaSheng) objectInputStream.readObject();
copy.setBirthday(new Date());
return copy;
} catch (Exception e) {
e.printStackTrace();
return null;
} finally {
IOUtils.closeQuietly(objectInputStream);
IOUtils.closeQuietly(byteArrayInputStream);
IOUtils.closeQuietly(objectOutputStream);
IOUtils.closeQuietly(byteArrayOutputStream);
}
}
}
5.2 测试类
package com.zhunongyun.toalibaba.designpatterns.prototype.deep;
public class DeepCloneTest {
public static void main(String[] args) {
QiTianDaSheng qiTianDaSheng = new QiTianDaSheng();
QiTianDaSheng clone = (QiTianDaSheng) qiTianDaSheng.clone();
System.out.println("深克隆,对象中的引用类型字段JinGuBang,内存地址是否相同:"
+ (qiTianDaSheng.getJinGuBang() == clone.getJinGuBang()));
}
}
6. 源码分析
在ArrayList中存在clone方法, 但属于浅克隆
public Object clone() {
try {
ArrayList<?> v = (ArrayList<?>) super.clone();
v.elementData = Arrays.copyOf(elementData, size);
v.modCount = 0;
return v;
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
}
7. Cloneable的使用
Cloneable是标记型的接口,它们内部都没有方法和属性,实现Cloneable来表示该对象能被克隆,能使用Object.clone()方法
如果没有实现Cloneable的类对象调用clone()就会抛出CloneNotSupportedException