Java对象拷贝

  • Post author:
  • Post category:java

在有些业务开发场景中需要对已有的数据进行copy(或者clone)操作,那么现有的copy方式有哪些?
开始讲之前需要进行知识点扫盲:
什么是引用?
Java中的引用类似C/C++中的指针,引用的值,指向内存中的一块空间。
对象引用赋值示例

    @Test
    public void referenceTest() {
        User source = new User();
        source.setName("young");
        source.setSex("male");
        User target = source;
        target.setSex("female");
        System.out.println(String.format("source:%s,target:%s", source.toString(), target.toString()));
        Assert.assertTrue(source.getSex().equals(target.getSex()));
    }

在这里插入图片描述
例子中,target的修改,同样引起了source的变化。
对象克隆赋值示例

@Test
    public void cloneTest() throws CloneNotSupportedException {
        User source = new User();
        source.setName("young");
        source.setSex("male");
        User target = User.class.cast(source.clone());
        target.setSex("female");
        System.out.println(String.format("source:%s,target:%s", source.toString(), target.toString()));
        Assert.assertTrue(source.getSex().equals(target.getSex()));
    }

在这里插入图片描述
例子中,target的修改,不会引起了source的变化。

什么是深克隆、浅克隆?
对象克隆赋值示例是简单情况的克隆。在了解深克隆与浅克隆之前,需要理解String 是final类。
如果一个类中除了String、Integer、Long等这些final类型的field,也存在普通类的实例,例如:
对象存在引用类型field克隆赋值示例

public class Account implements Cloneable {
    String accountId;
    String password;
    User user;
    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

为了限制文章篇幅,getter setter 不写了

    @Test
    public void cloneReferenceTest() throws CloneNotSupportedException {
        Account source = new Account ();
        source.accountId="source.accountId";
        source.password="source.password";
        source.user = new User();
        Account  target = Account .class.cast(source.clone());
        target.accountId="target.accountId";
        target.password="target.password";
        System.out.println(String.format("source:%s,target:%s", source.accountId, target.accountId));
        Assert.assertTrue(source.user==target.user);
    }

在这里插入图片描述
以上实例会发现,属性field并没有copy一份新的数据出来,这样是达不到我的期望。
这时候可以理解:
深克隆:会克隆出一个新的引用(user)实例
浅克隆:引用(user)不会生成新的实例

JDK中目前带有一些copy的API,进行验证一下:

    @Test
    public void apiCopyOf() {
        User source = new User();
        source.setName("young");
        source.setSex("male");
        List<User> sourceList = new ArrayList<>(1);
        sourceList.add(source);
        List<Object> targetList = Arrays.asList(Arrays.copyOf(sourceList.toArray(), sourceList.size()));
        User target = User.class.cast(targetList.get(0));
        target.setSex("female");
        System.out.println(String.format("apiCopyOf#source:%s,target:%s", source.toString(), target.toString()));
    }

在这里插入图片描述

    @Test
    public void streamCopyOf() {
        User source = new User();
        source.setName("young");
        source.setSex("male");
        List<User> sourceList = new ArrayList<>(1);
        sourceList.add(source);
        List<User> targetList = sourceList.stream().collect(Collectors.toList());
        User target = targetList.get(0);
        target.setSex("female");
        System.out.println(String.format("streamCopyOf#source:%s,target:%s", source.toString(), target.toString()));
    }

在这里插入图片描述
那么如何完全Copy一个对象的值呢?
答案是利用序列化与反序列化手段,将已存在对象实例序列化为字符串(或者二进制文件),再将字符串反序列化为一个对象实例在内存中。
基于以上的了解,进行对象数组的深克隆:

		<dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.9</version>
        </dependency>
public class CollectionsUtils {
    private static Logger logger = LogManager.getLogger(CollectionsUtils.class);
    private static ObjectMapper objectMapper = new ObjectMapper();

    /**
     * ArrayList 深克隆
     * 本质:序列化与反序列化
     * {@link ObjectMapper}版本可能会影响到这个方法的结果
     *
     * @param source
     * @param type
     * @param <T>
     * @return
     */
    public static <T> List<T> copyOf(List<T> source, Class<T> type) {
        List<T> target = new ArrayList<>(source.size());
        source.stream().forEach(d -> {
            try {
                String jsonString = objectMapper.writeValueAsString(d);
                target.add(objectMapper.readValue(jsonString, type));
            } catch (JsonProcessingException e) {
                logger.error("object convert to json string failed", e.getMessage());
                throw new RuntimeException(e);
            } catch (IOException e) {
                logger.error("json string convert to object failed", e.getMessage());
                throw new RuntimeException(e);
            }
        });
        return target;
    }
}
    @Test
    public void copyOf() {
        User source = new User();
        source.setName("young");
        source.setSex("male");
        List<User> sourceList = new ArrayList<>(1);
        sourceList.add(source);
        List<User> targetList = CollectionsUtils.copyOf(sourceList, User.class);
        User target = targetList.get(0);
        target.setSex("female");
        System.out.println(String.format("copyOf#source:%s,target:%s", source.toString(), target.toString()));
    }

在这里插入图片描述


版权声明:本文为u010730731原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。