咱们在 coding 的时候,常常会碰到这样的问题:我须要 copy 一个对象,而且我须要深度复制这个对象。html
固然,咱们能够特事特办,例如, new 一个对象,而后依次调用 set() 方法。json
可是,更多的时候,也许咱们但愿有一些通用的方法去达到这个目的。设计模式
首先,来看一下什么是深度复制?app
浅复制:对于对象的全部属性,复制全部的基本类型,可是对于引用类型,只是复制引用,可是这个引用仍然指向原来的对象。post
深复制:复制全部基本类型,对于引用类型,引用指向的对象也会被复制。性能
这里我总结了3种通用的深度复制策略:测试
@Data @Builder @NoArgsConstructor @AllArgsConstructor public class LiabilitySerializable implements Serializable { private static final long serialVersionUID = 3628603499527360136L; private String code; private String name; private String category; private boolean isMajor; }
@Data @Builder @NoArgsConstructor @AllArgsConstructor public class Policy implements Serializable { private static final long serialVersionUID = -3378513149295369473L; private String code; private int applicantAge; private LiabilitySerializable liability; private List<String> specialDescriptions; }
详见:设计模式(五)原型模式ui
这种方式可以完成深度复制,可是不推荐。url
在 第001弹:Java 中建立对象的4种方式 ,咱们提到了建立 Java 对象的4种方式。spa
这是经过序列化和反序列化完成深度复制的基础。
先把一个对象序列化,而后将它反序列化,获得一个新的对象。
使用这种方法,有2个重要的前提条件:
代码:
public static <T> T deepCopy1(T object) throws Exception { ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(object); ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return (T) ois.readObject(); }
咱们常常会使用一些结构化数据格式,做为平台之间数据交换的标准,典型的就是 JSON 以及 XML。
这些结构化数据格式,是有大量的第三方 JAR 包,提供了将字符串形式的数据,转化成指定对象的功能。
要注意的是,对于转换的对象,要提供一个无参构造器。
依赖:
<dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>1.9.13</version> </dependency>
代码:
public static <T> T deepCopy2(T object, Class<T> clazz) throws Exception { ObjectMapper mapper = new ObjectMapper(); String json = mapper.writeValueAsString(object); return mapper.readValue(json, clazz); }
public final class DeepCopyTest { private Policy buildPolicy() { LiabilitySerializable liability = new LiabilitySerializable.LiabilitySerializableBuilder().code("0001").name("Liability").category("XPXA").build(); String specialDescription1 = "text1"; String specialDescription2 = "text2"; List<String> specialDescriptions = new ArrayList<>(Arrays.asList(specialDescription1, specialDescription2)); return new Policy.PolicyBuilder().specialDescriptions(specialDescriptions).liability(liability).code("code001").applicantAge(18).build(); } private void assertDeepCopyPolicy(Policy policy1, Policy policy2) { Assertions.assertNotSame(policy1.getCode(), policy2.getCode()); Assertions.assertNotSame(policy1.getLiability(), policy2.getLiability()); Assertions.assertNotSame(policy1.getSpecialDescriptions(), policy2.getSpecialDescriptions()); Assertions.assertNotSame(policy1.getSpecialDescriptions().get(0), policy2.getSpecialDescriptions().get(0)); Assertions.assertNotSame(policy1.getSpecialDescriptions().get(1), policy2.getSpecialDescriptions().get(1)); } @Test void testDeepCopy1() throws Exception { Policy policy1 = buildPolicy(); Policy policy2 = DeepCopyUtils.deepCopy1(policy1); assertDeepCopyPolicy(policy1, policy2); } @Test void testDeepCopy2() throws Exception { Policy policy1 = buildPolicy(); Policy policy2 = DeepCopyUtils.deepCopy2(policy1, Policy.class); assertDeepCopyPolicy(policy1, policy2); } }
序列化和反序列化,须要每个参与的类都实现 Serializable 接口。
经过中间媒介转换在性能上有劣势。