文章目录
继承 Cloneable 接口,并重新实现 clone 方法
需要实体类继承 Cloneable 接口,并重新实现 clone() 方法。
需要注意的是,实体类中引用的指针类型元素也要继承 Cloneable 接口,并重新实现 clone() 方法,或者是重新新建一个对象赋值给元素。
@Data
public class CloneableEntity implements Cloneable{
private String stringValue;
private PointerEntity pointerEntity;
private List<String> listValue= new ArrayList<>();
//填充列表 用来比较克隆效率
public void initListValue() {
for (int i = 0; i < 10000; i++) {
this.listValue.add(UUID.randomUUID().toString());
}
}
@Override
public CloneableEntity clone() throws CloneNotSupportedException {
CloneableEntity clone = (CloneableEntity)super.clone();
// 指针类型的元素要做clone一份新的
if(null!=this.pointerEntity) {
clone.setPointerEntity(this.pointerEntity.clone());
}
// 创建一个新的对象给元素
clone.setListValue(new ArrayList<>(listValue));
return clone;
}
}
@Data
public class PointerEntity implements Cloneable{
private String uuid;
public PointerEntity(){
}
public PointerEntity(String uuid){
this.uuid = uuid;
}
@Override
protected PointerEntity clone() throws CloneNotSupportedException {
return (PointerEntity)super.clone();
}
}
克隆实例
克隆出来的新对象中的元素做任何修改,都不会影响原来对象的中元素的值。
Gson gson = new Gson();//用来打印对象
// Cloneable 深拷贝
CloneableEntity entity1 = new CloneableEntity();
entity1.setStringValue("1");
entity1.setPointerEntity(new PointerEntity(UUID.randomUUID().toString()));
entity1.setListValue(new ArrayList<String>(){{add("1");add("2");}});
CloneableEntity clone1 = entity1.clone();
//对克隆后的对象进行修改
clone1.setStringValue("1--1");
clone1.getListValue().add("3");
clone1.getPointerEntity().setUuid(UUID.randomUUID().toString());
System.out.println("entity1: "+gson.toJson(entity1));
System.out.println("clone1: "+gson.toJson(clone1));
-------------------------------
entity1: {"stringValue":"1","pointerEntity":{"uuid":"27bb47c6-1ad3-4b79-a408-509f2163b5fa"},"listValue":["1","2"]}
clone1: {"stringValue":"1--1","pointerEntity":{"uuid":"a3495d1d-e32c-4dee-be57-0a8a3f30678c"},"listValue":["1","2","3"]}
使用流进行序列化、反序列化
克隆的类必须支持序列化,类中的元素也必须支持序列化。否则在克隆是会爆出不支持序列化异常。
@Data
public class Serialzable2Entity implements Serializable {
private String stringValue;
private SerialzablePointerEntity pointerEntity;
private List<String> listValue= new ArrayList<>();
//填充列表 用来比较克隆效率
public void initListValue() {
for (int i = 0; i < 10000; i++) {
this.listValue.add(UUID.randomUUID().toString());
}
}
}
@Data
public class SerialzablePointerEntity implements Serializable {
private String uuid;
public SerialzablePointerEntity(){
}
public SerialzablePointerEntity(String uuid){
this.uuid = uuid;
}
}
克隆实例
Gson gson = new Gson();//用来打印对象
//流深拷贝
Serialzable2Entity entity4 = new Serialzable2Entity();
entity4.setStringValue("41");
entity4.setPointerEntity(new SerialzablePointerEntity(UUID.randomUUID().toString()));
entity4.setListValue(new ArrayList<String>(){{add("41");add("42");}});
Serialzable2Entity clone4 ;
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
) {
oos.writeObject(entity4);
oos.flush();
try (
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
) {
clone4 = (Serialzable2Entity) ois.readObject();
}
}
clone4.setStringValue("41--1");
clone4.getListValue().add("43");
clone4.getPointerEntity().setUuid(UUID.randomUUID().toString());
System.out.println("entity4: " + gson.toJson(entity4));
System.out.println("clone4: " + gson.toJson(clone4));
---------------------------------------
entity4: {"stringValue":"41","pointerEntity":{"uuid":"af3c287f-1289-4fb3-947f-f5b7684ef07e"},"listValue":["41","42"]}
clone4: {"stringValue":"41--1","pointerEntity":{"uuid":"0549e071-d7df-469f-baae-578e9aa89194"},"listValue":["41","42","43"]}
使用 Apache Commons Lang 序列化工具
Apache Commons Lang 自带了序列化工具类 SerializationUtils,其中的 clone() 方法允许我们对对象进行深拷贝。
其实它的原理和上面流的序列化、反序列化是一样的,只不过多了一些类型转化及其他操作在里面。因此它
同样需要克隆的类必须支持序列化,类中的元素也必须支持序列化。否则在克隆是会爆出不支持序列化异常。
克隆实例
Gson gson = new Gson();//用来打印对象
// Apache Commons Lang序列化深拷贝 拷贝对象和对象中所有涉及到的指针类型的对象都要实现序列化
Serialzable2Entity entity2 = new Serialzable2Entity();
entity2.setStringValue("11");
entity2.setPointerEntity(new SerialzablePointerEntity(UUID.randomUUID().toString()));
entity2.setListValue(new ArrayList<String>(){{add("11");add("12");}});
Serialzable2Entity clone2 = SerializationUtils.clone(entity2);
//对克隆后的对象进行修改
clone2.setStringValue("11--1");
clone2.getListValue().add("13--1");
clone2.getPointerEntity().setUuid(UUID.randomUUID().toString());
System.out.println("entity2: "+gson.toJson(entity2));
System.out.println("clone2: "+gson.toJson(clone2));
------------------------------------------
entity2: {"stringValue":"11","pointerEntity":{"uuid":"303e0bff-ad81-4b56-a1db-311cf894b189"},"listValue":["11","12"]}
clone2: {"stringValue":"11--1","pointerEntity":{"uuid":"bb42fd5b-d0ce-483a-9f34-d32e1b0eab5c"},"listValue":["11","12","13--1"]}
使用 Gson 序列化、反序列化
使用 Gson 序列化、反序列化去进行克隆,克隆对象不需要做任何扩展。
@Data
public class NormalEntity{
private String stringValue;
private NormalPointerEntity pointerEntity;
private List<String> listValue= new ArrayList<>();
/**
* 初始化列表
*/
public void initListValue() {
for (int i = 0; i < 10000; i++) {
this.listValue.add(UUID.randomUUID().toString());
}
}
}
@Data
public class NormalPointerEntity{
private String uuid;
public NormalPointerEntity(){
}
public NormalPointerEntity(String uuid){
this.uuid = uuid;
}
}
Gson gson = new Gson();
// Gson 序列化反序列化深拷贝 对对象没有要求
NormalEntity entity3 = new NormalEntity();
entity3.setStringValue("21");
entity3.setPointerEntity(new NormalPointerEntity(UUID.randomUUID().toString()));
entity3.setListValue(new ArrayList<String>(){{add("21");add("22");}});
NormalEntity clone3 = gson.fromJson(gson.toJson(entity3 ), NormalEntity.class);
//修改克隆后的对象
clone3.setStringValue("21--1");
clone3.getListValue().add("23");
clone3.getPointerEntity().setUuid(UUID.randomUUID().toString());
System.out.println("entity3: "+gson.toJson(entity3));
System.out.println("clone3: "+gson.toJson(clone3));
---------------------------
entity3: {"stringValue":"21","pointerEntity":{"uuid":"b81d7ee9-54db-4d51-b09d-0a61adce4518"},"listValue":["21","22"]}
clone3: {"stringValue":"21--1","pointerEntity":{"uuid":"276de4f8-0b23-4435-9225-dfc836311064"},"listValue":["21","22","23"]}
优劣对比
首先效率上我们进行对比
StopWatch stopWatch = new StopWatch();
// Cloneable 深拷贝
CloneableEntity entity1 = new CloneableEntity();
entity1.initListValue();
stopWatch.start("Cloneable 深拷贝");
CloneableEntity clone1 = entity1.clone();
stopWatch.stop();
// 流深拷贝
Serialzable2Entity entity4 = new Serialzable2Entity();
entity4.initListValue();
Serialzable2Entity clone4 ;
stopWatch.start(" 流深拷贝");
try (ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
) {
oos.writeObject(entity4);
oos.flush();
try (
ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bis);
) {
clone4 = (Serialzable2Entity) ois.readObject();
}
}
stopWatch.stop();
// Apache Commons Lang序列化深拷贝 拷贝对象和对象中所有涉及到的指针类型的对象都要实现序列化
Serialzable2Entity entity2 = new Serialzable2Entity();
entity2.initListValue();
stopWatch.start("Apache Commons Lang序列化深拷贝");
Serialzable2Entity clone2 = SerializationUtils.clone(entity2);
stopWatch.stop();
// Gson 序列化反序列化深拷贝 对对象没有要求
NormalEntity entity3 = new NormalEntity();
entity3.initListValue();
stopWatch.start(" Gson 序列化反序列化深拷贝");
Gson gson = new Gson();//用来打印对象
NormalEntity clone3 = gson.fromJson(gson.toJson(entity3 ), NormalEntity.class);
stopWatch.stop();
System.out.println(stopWatch.prettyPrint());
-----------------------------------
---------------------------------------------
ns % Task name
---------------------------------------------
000029100 000% Cloneable 深拷贝
048922700 023% 流深拷贝
017047500 008% Apache Commons Lang序列化深拷贝
150727000 070% Gson 序列化反序列化深拷贝
根据耗时可以看出来,使用继承 Cloneable 接口克隆的效率最高,其次是使用流序列化和 SerializationUtils ,使用Gson序列化最慢。
其中要注意的是流深拷贝 和 SerializationUtils 的效率实际上应该是相差不大的。因为本质上都是使用的流去进行序列化和反序列化。但是由于在程序启动后第一次加载 IO 类时,会消耗一部分时间,所以在执行 SerializationUtils 克隆时,耗时比流序列化短。
当我们调换流序列化和 SerializationUtils 的执行顺序时,会发现流序列化消耗的时间比 SerializationUtils 短。