美文网首页
java 深拷贝和浅拷贝

java 深拷贝和浅拷贝

作者: 尹楷楷 | 来源:发表于2021-05-13 10:14 被阅读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的属性 ====
小明
行政部

相关文章

  • java 对象的拷贝

    拷贝:即复制 对象拷贝:即对象复制 java 对象拷贝分类:浅拷贝、深拷贝 java 对象的浅拷贝和深拷贝针对包含...

  • Java基础 - 深拷贝和浅拷贝

    Java 的深拷贝和浅拷贝 什么是深拷贝、浅拷贝 (深克隆、浅克隆)? 在 Java 中,数据类型分为 基本数据类...

  • java中的深拷贝和浅拷贝

    简单记录一下java中的深拷贝和浅拷贝,深拷贝和浅拷贝只是针对对象而言的. 1 深拷贝代码 2 浅拷贝代码 3 测...

  • iOS深拷贝(MutableCopy)与浅拷贝(Copy)的区别

    深拷贝和浅拷贝的概念 iOS中有深拷贝和浅拷贝的概念,那么何为深拷贝何为浅拷贝呢?浅拷贝:浅拷贝并不拷贝对象本身,...

  • Java------List的深拷贝与浅拷贝

    Java的浅拷贝(Shallow Copy)、深拷贝(Deep Copy)。 浅拷贝(Shallow Copy) ...

  • Java的浅拷贝和深拷贝

    首先需要明白,浅拷贝和深拷贝都是针对一个已有对象的操作。那先来看看浅拷贝和深拷贝的概念。 在 Java 中,除了基...

  • iOS面试题-第二页

    11.深拷贝和浅拷贝的理解. 深拷贝;拷贝的内容. 浅拷贝:拷贝的指针. 深拷贝如: NSMutableDicti...

  • 深克隆--何时是尽头?

    As we all know Java有两种拷贝,浅拷贝和深拷贝,高大上的叫法也叫浅克隆和深克隆。 深克隆有时会碰...

  • iOS - copy 与 mutableCopy

    一说到拷贝,就不得不提浅拷贝和深拷贝。 何谓浅拷贝?何谓深拷贝? 往简单的说: 浅拷贝:拷贝地址。 深拷贝:拷贝内...

  • 关于Java的浅拷贝和深拷贝

    浅拷贝和深拷贝是什么? 浅拷贝和深拷贝都是针对已经存在了的对象的操作,在java中,基本数据类型有八种,和引用数据...

网友评论

      本文标题:java 深拷贝和浅拷贝

      本文链接:https://www.haomeiwen.com/subject/sovidltx.html