二、序列化
另一种实现深度拷贝的方式就是序列化,无论是Jdk的序列化还是其他方式的序列化都需要实现自 java.io.Serializable接口,并且设置自己的serialVersionUID,并且保证项目中不能有相同的值(很多开发的时候,基于原来的类copy过来后需要进行修改),如下
public class DeepCopyEntity implements Cloneable, Serializable { private static final long serialVersionUID = 6172279441386879379L; }
三、深度拷贝的方式
1、new关键字
实现对象的深度拷贝,就是对象的每一层属性的内存地址都不相同,那么基于new 对象,再每一层设置new的属性对象。也是可以实现的,或者基于反射的方式,并且性能也是比较高的。需要注意jdk 6及之前的反射性能比较差。
优点:性能高,缺点:就是每个对象都需要new,并且每一层都需要用setter等进行赋值【硬编码】。
2、Clone
优点:性能高,缺点:所有层级只要有属性对象就需要实现Clone,并且重写clone方法。如果对象有七八层,其中每一层的每一个地方没有注意到就可能非深拷贝。
3、JDK 序列化
jdk序列化只需要基于ObjectOutputStream将原对象流写出去(写入本地磁盘),再基于ObjectInputStream将对象流读回来即可。如下
/** * 深层拷贝 - 需要类继承序列化接口 * @param <T> 对象类型 * @param obj 原对象 * @return 深度拷贝的对象 * @throws Exception * @see java.io.Closeable * @see AutoCloseable 不用进行关闭 */ @SuppressWarnings("unchecked") public static <T> T copyImplSerializable(T obj) throws Exception { ByteArrayOutputStream baos = null; ObjectOutputStream oos = null; ByteArrayInputStream bais = null; ObjectInputStream ois = null; Object o = null; //如果子类没有继承该接口,这一步会报错 try { baos = new ByteArrayOutputStream(); oos = new ObjectOutputStream(baos); oos.writeObject(obj); bais = new ByteArrayInputStream(baos.toByteArray()); ois = new ObjectInputStream(bais); o = ois.readObject(); return (T) o; } catch (Exception e) { throw new Exception("对象中包含没有继承序列化的对象"); } }
优点:不需要像克隆和new一样单独开发,缺点:性能比较差
4、kyro序列化
kyro需要单独引入maven依赖,如
<dependency> <groupId>com.esotericsoftware</groupId> <artifactId>kryo</artifactId> <version>5.0.0-RC9</version> </dependency>
使用时需要创建 Kryo对象【 Kryo kryo = new Kryo(); 】,只是该对象是非线程安全的,所有如果在项目中使用时,最好放到ThreadLocal中进行创建。使用就比较简单了
public static <T> T copyByKryo(T source){ return kryo.copy(source); }
优点:性能较高, 缺点:需要单独引入maven,性能比new 和clone的低一点
5、Json序列化
项目上使用Json 进行 redis、rpc调用(如 Spring Cloud Feign) 进行序列化和反序列化是比较常用的,但是如果仅仅是本地深度拷贝,则使用该方式性能是最差的。可以在下面进行比较,各种json框架的序列化方式都差不多。