• 第004弹:几种通用的深度复制的方法


    • 引出问题:

      我们在 coding 的时候,经常会碰到这样的问题:我需要 copy 一个对象,并且我需要深度复制这个对象。

      当然,我们可以特事特办,例如, new 一个对象,然后依次调用 set() 方法。

      但是,更多的时候,也许我们希望有一些通用的方法去达到这个目的。

      首先,来看一下什么是深度复制?

    • 深度复制和浅度复制(Deep Copy & Shallow Copy)

      浅复制:对于对象的所有属性,复制所有的基本类型,但是对于引用类型,只是复制引用,但是这个引用仍然指向原来的对象。

      深复制:复制所有基本类型,对于引用类型,引用指向的对象也会被复制。

    • 通用的深度复制策略:

      这里我总结了3种通用的深度复制策略:

    1. 实现一系列优秀的 clone() 方法。
    2. 使用序列化和反序列化完成深复制。
    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;
    }
    • 实现一系列优秀的 clone() 方法

      详见:设计模式(五)原型模式

      这种方式能够完成深度复制,但是不推荐。

    • 序列化和反序列化

      在 第001弹:Java 中创建对象的4种方式 ,我们提到了创建 Java 对象的4种方式。

      这是通过序列化和反序列化完成深度复制的基础。

      先把一个对象序列化,然后将它反序列化,得到一个新的对象。

      使用这种方法,有2个重要的前提条件:

    • 被复制的对象,以及这个对象的所有属性,都要间接或者直接地实现 Serializable 接口。
    • 被复制的对象,不能随意地实现 writeReplace() 和 readResoleve() 方法。

      代码:

        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 接口。

      通过中间媒介转换在性能上有劣势。

  • 相关阅读:
    PCL点云
    unity碰撞,刚体笔记
    动画剪辑 状态配置 脚本编写2
    unity中动画剪辑,状态机关系配置,脚本编写方式1
    unity给物体着色加纹理图 material(材质)
    C++/Java小白解Leetcode题,发现了知识盲区……
    NLP之统计句法分析(PCFG+CYK算法)
    Java:基于TCP协议网络socket编程(实现C/S通信)
    Java实现:抛开jieba等工具,写HMM+维特比算法进行词性标注
    Java多线程技术:实现多用户服务端Socket通信
  • 原文地址:https://www.cnblogs.com/jing-an-feng-shao/p/10813639.html
Copyright © 2020-2023  润新知