原型模式中要实现深拷贝对象,一般是要重写clone()方法,因为有一些对象是没有实现Cloneable接口的,比如String,Integer,BigDecimal,这些常用的类都没有实现Cloneable接口。
但是很多情况下,如果重写clone()方法实现深拷贝,又会重新new对象(为了使引用对象指向是一个新的内存地址),像下面这样:
@Override
protected User clone() throws CloneNotSupportedException {
User cloneUser = (User) super.clone();
cloneUser.setAge(new Integer(this.age));
cloneUser.setId(new Long(this.id));
cloneUser.setMoney(new BigDecimal(this.money.toString()));
cloneUser.setName(new String(this.name));
return cloneUser;
}
这就有点违背了原型模式的初衷,原型本来就是为了避免new对象时进行复杂的构造,这里重写clone()不仅没有避免,并且有new对象了。
这种情况下,我们就要使用对象序列化的方式来实现深拷贝,而最为推荐的是FastByteArrayOutputStream和ByteArrayOutputStream对象来实现深拷贝。
那么他们到底效率怎么样呢?
上代码:
这是要拷贝的对象:
public class User implements Serializable,Cloneable{
private Integer age;
private String name;
private Long id;
private BigDecimal money;
public BigDecimal getMoney() {
return money;
}
public void setMoney(BigDecimal money) {
this.money = money;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(age, user.age) &&
Objects.equals(name, user.name) &&
Objects.equals(id, user.id) &&
Objects.equals(money, user.money);
}
@Override
public int hashCode() {
return Objects.hash(age, name, id, money);
}
public User() {
this.age = 0;
this.name = "";
this.id = 1L;
this.money = BigDecimal.ZERO;
}
public User(Integer age, String name, Long id, BigDecimal money) {
this.age = age;
this.name = name;
this.id = id;
this.money = money;
}
}
下面是拷贝工具类:
public class GetModelUtil {
private static FastByteArrayOutputStream fastByteArrayOutputStream;
private static ObjectOutputStream outputStreamOfFast;
private static ObjectInputStream objectInputStreamOfFast;
private static ByteArrayOutputStream commonbyteArrayOutputStream;
private static ByteArrayInputStream commonbyteArrayInputStream;
private static ObjectOutputStream outputStreamOfCommon;
private static ObjectInputStream objectInputStreamOfCommon;
// 为了提高效率,事先初始化内存输出对象
static {
fastByteArrayOutputStream = new FastByteArrayOutputStream();
commonbyteArrayOutputStream = new ByteArrayOutputStream();
}
// FastByteArrayOutputStream 拷贝
public static Object fastDeepClone(Object orig) throws IOException {
Object obj = null;
try {
// 初始化 ObjectOutputStream 输出流对象
outputStreamOfFast = new ObjectOutputStream(fastByteArrayOutputStream);
// 写入对象到字节数组中,进行序列化
outputStreamOfFast.writeObject(orig);
// 刷新输出流
outputStreamOfFast.flush();
//从这个拷贝对象的流中 获取输入流
objectInputStreamOfFast = new ObjectInputStream(fastByteArrayOutputStream.getInputStream());
//从输入流中读出这个对象
obj = objectInputStreamOfFast.readObject();
// 因为测试的时候要多次读写,所以不关闭输入流和输出流
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
//返回拷贝的对象
return obj;
}
// ByteArrayOutputStream 拷贝
public static Object commonDeepClone(Object orig) throws IOException {
Object obj = null;
try {
// 初始化 ObjectOutputStream 输出流对象
outputStreamOfCommon = new ObjectOutputStream(commonbyteArrayOutputStream);
// 将指定的对象写入字节数组输出,进行序列化
outputStreamOfCommon.writeObject(orig);
// 刷新输出流
outputStreamOfCommon.flush();
// 使用字节数组初始化 ByteArrayInputStream 输入流对象
commonbyteArrayInputStream = new ByteArrayInputStream(commonbyteArrayOutputStream.toByteArray());
// 使用字节数组初始化 ByteArrayInputStream 输入流对象
objectInputStreamOfCommon = new ObjectInputStream(commonbyteArrayInputStream);
// 从内存中读出对象
obj = objectInputStreamOfCommon.readObject();
// 因为测试的时候要多次读写,所以不关闭输入流和输出流
} catch (ClassNotFoundException cnfe) {
cnfe.printStackTrace();
}
//返回拷贝的对象
return obj;
}
}
测试代码:
public static void main(String[] args) throws IOException, CloneNotSupportedException {
User user = new User();
user.setAge(11);
user.setId(12L);
user.setName("花花");
long commonCloneStart = System.currentTimeMillis();
for (int i = 0 ; i <= 1000 ; i++){
User userOfClone = (User) GetModelUtil.commonDeepClone(user);
}
System.out.println("使用ByteArrayOutputStream深克隆花费时间:" + (System.currentTimeMillis() - commonCloneStart));
long fastCloneStart = System.currentTimeMillis();
for (int i = 0 ; i <= 1000 ; i++){
User userOfClone = (User) GetModelUtil.fastDeepClone(user);
}
System.out.println("使用FastByteArrayOutputStream深克隆花费时间:" + (System.currentTimeMillis() - fastCloneStart));
}
运行结果:
使用ByteArrayOutputStream深克隆花费时间:224
使用FastByteArrayOutputStream深克隆花费时间:57
很明显,使用FastByteArrayOutputStream拷贝对象效率明显高于ByteArrayOutputStream。
原因:
FastByteArrayOutputStream内部实现由一个LinkedList<byte[]>组成,每一次扩容中分配一个数组的空间,并当该数据放入到List中。需要分配的数组长度为调用FastByteArrayOutputStream的write方法决定。而ByteArrayOutputStream内部实现为一个数组每一次扩容需要重新分配空间并将数据复制到新数组中,这就是FastByteArrayOutputStream比ByteArrayOutputStream主要区别。
FastByteArrayOutputStream 部分源码
public class FastByteArrayOutputStream extends OutputStream {
private static final int DEFAULT_BLOCK_SIZE = 256;
// The buffers used to store the content bytes
private final LinkedList<byte[]> buffers = new LinkedList<>();
...
}
ByteArrayOutputStream部分源码:
public class ByteArrayOutputStream extends OutputStream {
/**
* The buffer where data is stored.
*/
protected byte buf[];
...
}