java 深拷贝和浅拷贝

2021-05-13  本文已影响0人  尹楷楷

浅拷贝: 不额外创建子对象,只是把子对象的引用拷贝过去
深拷贝: 创建新的子对象并拷贝属性

如果把java bean划分为 DTO、DO、VO 的话就避免不了对象的copy了。一般选择的spring的工具类都是浅拷贝。当对象内部还有对象 时只能copy内部对象的引用,这样的话不利于灵活修改。

浅拷贝模式,分两步:

spring 的BeanUtils.copyProperties(sourceBean, destBean)只针对Person对象,所以只会把左边Person对象的属性值拷贝到右边,至于子对象department,也只是拷贝了引用。具体看下面的BeanUtils.copyProperties例子。

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.beans.BeanUtils;

public class MyBeanUtilsTest {

    private static List<Person> list;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Person implements Serializable {
        private String name;
        private Integer age;
        private Department department;
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Department implements Serializable{
        private String name;
    }

    static {
        list = new ArrayList<>();
        list.add(new Person("小明", 18, new Department("行政部")));
    }

    public static void main(String[] args) {
        Person bean = list.get(0);
        Person copyBean = new Person();
        BeanUtils.copyProperties(bean, copyBean);
        System.out.println(bean == copyBean);

        System.out.println("==== copyBean的属性 ====");
        System.out.println(copyBean.getName());
        System.out.println(copyBean.getDepartment().getName());

        bean.setName("小亮");
        bean.getDepartment().setName("研发部");

        System.out.println("==== sourceBean修改后,copyBean的属性 ====");
        System.out.println(copyBean.getName());
        System.out.println(copyBean.getDepartment().getName());
    }
}

false
==== copyBean的属性 ====
小明
行政部
==== sourceBean修改后,copyBean的属性 ====
小明
研发部

修改bean的department 同时也把copyBean 的department 修改了。
使用Spring提供的BeanUtils进行数据拷贝,但它有两个问题:

深拷贝如何实现

1、ObjectOutputStream.writeObject()

    public static <T> T deepCopyObj(T object) throws IOException, ClassNotFoundException {
        ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(byteOut);
        out.writeObject(object);
        ByteArrayInputStream byteIn = new ByteArrayInputStream(byteOut.toByteArray());
        ObjectInputStream in = new ObjectInputStream(byteIn);
        T dest = (T) in.readObject();
        return dest;
    }

这次修改为调用深拷贝实现:


    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Person bean = list.get(0);
        Person copyBean = MyBeanUtils.deepCopyObj(bean);
        System.out.println(bean == copyBean);

        System.out.println("==== copyBean的属性 ====");
        System.out.println(copyBean.getName());
        System.out.println(copyBean.getDepartment().getName());

        bean.setName("小亮");
        bean.getDepartment().setName("研发部");

        System.out.println("==== sourceBean修改后,copyBean的属性 ====");
        System.out.println(copyBean.getName());
        System.out.println(copyBean.getDepartment().getName());
    }

false
==== copyBean的属性 ====
小明
行政部
==== sourceBean修改后,copyBean的属性 ====
小明
行政部

修改bean的department 也没有影响到copyBean 的department,达到了深拷贝的目的!

2、JSON序列化、反序列化也是可以的。使用ObjectMapper

先引入jackson

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.6</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.9.6</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.6</version>
    </dependency>
  </dependencies>

使用

    public static void main(String[] args) throws IOException {
        Person bean = list.get(0);
        ObjectMapper objectMapper = new ObjectMapper();
        String copyBeanStr = objectMapper.writeValueAsString(bean);
        Person copyBean = objectMapper.readValue(copyBeanStr, new TypeReference<Person>() {});
        System.out.println(bean == copyBean);

        System.out.println("==== copyBean的属性 ====");
        System.out.println(copyBean.getName());
        System.out.println(copyBean.getDepartment().getName());

        bean.setName("小亮");
        bean.getDepartment().setName("研发部");

        System.out.println("==== sourceBean修改后,copyBean的属性 ====");
        System.out.println(copyBean.getName());
        System.out.println(copyBean.getDepartment().getName());
    }

false
==== copyBean的属性 ====
小明
行政部
==== sourceBean修改后,copyBean的属性 ====
小明
行政部

3、使用二方库 MapStruct

import java.util.List;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.Mappings;
import org.mapstruct.factory.Mappers;
@Mapper
public interface PersonConverter {
    PersonConverter INSTANCE = Mappers.getMapper(PersonConverter.class);
    @Mappings({
        @Mapping(source = "name", target = "name"),
        @Mapping(source = "age", target = "age"),
        @Mapping(source = "department.name", target = "department.name"),
    })
    Person domain2dto(Person person);

    List<Person> domain2dto(List<Person> people);

}
    public static void main(String[] args) throws IOException {
        Person bean = list.get(0);
        Person copyBean = PersonConverter.INSTANCE.domain2dto(bean);
        System.out.println(bean == copyBean);
        System.out.println("==== copyBean的属性 ====");
        System.out.println(copyBean.getName());
        System.out.println(copyBean.getDepartment().getName());
        bean.setName("小亮");
        bean.getDepartment().setName("研发部");
        System.out.println("==== sourceBean修改后,copyBean的属性 ====");
        System.out.println(copyBean.getName());
        System.out.println(copyBean.getDepartment().getName());
    }
}

false
==== copyBean的属性 ====
小明
行政部
==== sourceBean修改后,copyBean的属性 ====
小明
行政部

上一篇下一篇

猜你喜欢

热点阅读