前言
Java的深度拷贝大致分为克隆(实现Java的Clone接口)和序列化(实现Java的Serializable接口)两种,但是基于不同的序列化方式,有可以延伸出几种方式。下面分析一下每种的注意事项和性能对比【当前电脑为4核16G,只是当前使用main方法单线程测试】。
一、拷贝和深浅拷贝
可以使用Java native方法提供的Clone方式进行对象的拷贝,其性能是最高的,甚至高过new 关键字。使用new关键字创建对象,如果是第一次创建则会经历类加载机制的双亲委派(加载、验证、准备、解析、初始化)。即使非第一次创建也会经历(常量池判断,内存分配,值初始化,init方法调用,栈中对象的引用等)等过程。
我们需要继承自Clone接口,重写Object的clone方法。如下
public class DeepCopyEntity implements Cloneable { @Override protected DeepCopyEntity clone() { try { return (DeepCopyEntity)super.clone(); } catch (CloneNotSupportedException e) { log.info("没有实现克隆接口"); return null; } } }
但是我们在使用的时候,需要每个对象都编写这样的代码。可以优化为继承自类似下面的 CloneSupport 类(前体是没有继承其他的类)
public class CloneSupport<T> implements Cloneable { @SuppressWarnings("unchecked") @Override public T clone() { try { return (T) super.clone(); } catch (CloneNotSupportedException e) { throw new CloneRuntimeException(e); } } }
但是即使是克隆之后的对象也是浅拷贝。即对象的属性如果是非基本数据类型和String的情况下,新老对象的对象属性的内存地址任然相同,则任何一个对象改变其值之后,另一个对象的值也就是改变了,这很多时候可能是我们不想要的。那么需要进行深度的拷贝。则需要其属性对象的类也继承自Clone接口,并且重新clone方法。如下(是我项目中使用的)
public class PurOrderSkuBO implements Serializable, Cloneable { @Override public PurOrderSkuBO clone() { try { final PurOrderSkuBO clone = (PurOrderSkuBO) super.clone(); clone.purOrderSkuDTO = purOrderSkuDTO.clone(); clone.productList = productList.stream().map(PurOrderItemBO::clone).collect(Collectors.toList()); return clone; } catch (CloneNotSupportedException e) { return new PurOrderSkuBO(); } } private PurOrderSkuDTO purOrderSkuDTO; private List<PurOrderItemBO> productList; }
public class PurOrderSkuDTO extends CloneSupport<PurOrderSkuDTO> {}