第004彈:幾種通用的深度複製的方法

  • 引出問題:

  咱們在 coding 的時候,常常會碰到這樣的問題:我須要 copy 一個對象,而且我須要深度複製這個對象。html

  固然,咱們能夠特事特辦,例如, new 一個對象,而後依次調用 set() 方法。json

  可是,更多的時候,也許咱們但願有一些通用的方法去達到這個目的。設計模式

 

  首先,來看一下什麼是深度複製?app

 

  • 深度複製和淺度複製(Deep Copy & Shallow Copy)

  淺複製:對於對象的全部屬性,複製全部的基本類型,可是對於引用類型,只是複製引用,可是這個引用仍然指向原來的對象。post

  深複製:複製全部基本類型,對於引用類型,引用指向的對象也會被複制。性能

 

  • 通用的深度複製策略:

  這裏我總結了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() 方法

  詳見:設計模式(五)原型模式ui

  這種方式可以完成深度複製,可是不推薦。url

 

  • 序列化和反序列化

  在 第001彈:Java 中建立對象的4種方式 ,咱們提到了建立 Java 對象的4種方式。spa

  這是經過序列化和反序列化完成深度複製的基礎。

  先把一個對象序列化,而後將它反序列化,獲得一個新的對象。

 

  使用這種方法,有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 接口。

  經過中間媒介轉換在性能上有劣勢。

相關文章
相關標籤/搜索