浅拷贝与深拷贝

在工作开发中,我们有时候需要将一份原始数据复制成一份新数据,再对新数据进行部分修改,这里就引出浅拷贝与深拷贝两个概念。

需要注意的是,浅拷贝和深拷贝是编程中的概念,它们并不局限于某一种语言,每种语言都有自己的浅拷贝/深拷贝方式。

什么是浅拷贝?

  • 对于基本类型数据,浅拷贝复制了数据本身,而对于对象类型数据,浅拷贝只复制了对象的引用,而不是对象本身

  • 因此,如果修改了浅拷贝后数据中的对象,那么原始数据中的对象也会受到影响

什么是深拷贝

  • 对于基本类型数据,深拷贝复制了数据本身,而对于对象类型数据,深拷贝会递归复制对象的所有元素,包括对象类型

  • 深拷贝后的数据是完全和原始数据独立的,因此对它进行任何操作都是安全的

Java中实现浅拷贝

  1. 使用Object.clone()方法

    • 代码示例

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      public class Order implements Cloneable {
      private String orderName;
      private Long orderNo;
      private Address address;

      // 省略getter/setter

      @Override
      protected Object clone() throws CloneNotSupportedException {
      return super.clone();
      }
      }

      public class Address {
      private String country;
      private String province;
      private String city;
      private String district;
      private String detailAddress;

      // 省略getter/setter
      }

      public class TestCopy {
      public static void main(String[] args) throws CloneNotSupportedException {
      Address address = new Address();
      address.setCountry("中国");
      address.setProvince("浙江省");
      address.setCity("杭州市");
      address.setDistrict("西湖区");
      address.setDetailAddress("凤起路");

      Order order = new Order();
      order.setOrderName("双十一好物订单");
      order.setOrderNo(202300156L);
      order.setAddress(address);

      System.out.println("order: " + order);
      System.out.println("address: " + address);
      System.out.println("detailAddress: " + address.getDetailAddress());

      Order copyOrder = (Order) order.clone();

      System.out.println("copyOrder: " + copyOrder);
      System.out.println("copyAddress: " + copyOrder.getAddress());

      copyOrder.getAddress().setDetailAddress("武林广场");
      System.out.println("detailAddress: " + address.getDetailAddress());
      }
      }
    • 运行结果

      1
      2
      3
      4
      5
      6
      order: com.codecho.copy.Order@677327b6
      address: com.codecho.copy.Address@14ae5a5
      detailAddress: 凤起路
      copyOrder: com.codecho.copy.Order@7f31245a
      copyAddress: com.codecho.copy.Address@14ae5a5
      detailAddress: 武林广场

      ==可以看出,使用Object.clone()方法复制出了一个新的Order对象,但是Order对象内部的Address还是原来的Address对象,并且对新Order对象内部Address进行修改会影响到原Address对象,说明Object.clone()方法只是拷贝了对象的引用,并没有拷贝对象本身。==

  2. 使用apache或spring提供的BeanUtils工具类

    • 代码示例

      1
      2
      3
      4
      5
      <dependency>
      <groupId>commons-beanutils</groupId>
      <artifactId>commons-beanutils</artifactId>
      <version>1.9.4</version>
      </dependency>
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      public class TestCopy {

      public static void main(String[] args) throws InvocationTargetException, IllegalAccessException {
      Address address = new Address();
      address.setCountry("中国");
      address.setProvince("浙江省");
      address.setCity("杭州市");
      address.setDistrict("西湖区");
      address.setDetailAddress("凤起路");

      Order order = new Order();
      order.setOrderName("双十一好物订单");
      order.setOrderNo(202300156L);
      order.setAddress(address);

      System.out.println("order: " + order);
      System.out.println("address: " + address);
      System.out.println("detailAddress: " + address.getDetailAddress());

      Order copyOrder = new Order();
      BeanUtils.copyProperties(copyOrder, order);

      System.out.println("copyOrder: " + copyOrder);
      System.out.println("copyAddress: " + copyOrder.getAddress());

      copyOrder.getAddress().setDetailAddress("武林广场");
      System.out.println("detailAddress: " + address.getDetailAddress());
      }

      }
    • 运行结果

      1
      2
      3
      4
      5
      6
      order: com.codecho.copy.Order@330bedb4
      address: com.codecho.copy.Address@2503dbd3
      detailAddress: 凤起路
      copyOrder: com.codecho.copy.Order@685f4c2e
      copyAddress: com.codecho.copy.Address@2503dbd3
      detailAddress: 武林广场

      ==和Object.clone()方法一样,BeanUtils.copyProperties()方法也是浅拷贝==

Java中实现深拷贝

  1. 序列化和反序列化

    • 代码示例

      ==注意:Order和Address需要实现Serializable接口==

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      public class Order implements Serializable {
      ...
      }

      public class Address implements Serializable {
      ...
      }

      public static void main(String[] args) throws IOException, ClassNotFoundException {
      Address address = new Address();
      address.setCountry("中国");
      address.setProvince("浙江省");
      address.setCity("杭州市");
      address.setDistrict("西湖区");
      address.setDetailAddress("凤起路");

      Order order = new Order();
      order.setOrderName("双十一好物订单");
      order.setOrderNo(202300156L);
      order.setAddress(address);

      System.out.println("order: " + order);
      System.out.println("address: " + address);
      System.out.println("detailAddress: " + address.getDetailAddress());

      Order copyOrder = deepCopy(order);

      System.out.println("copyOrder: " + copyOrder);
      System.out.println("copyAddress: " + copyOrder.getAddress());

      copyOrder.getAddress().setDetailAddress("武林广场");
      System.out.println("detailAddress: " + address.getDetailAddress());
      }


      public static Order deepCopy(Order source) throws IOException, ClassNotFoundException {
      ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
      ObjectOutputStream out = new ObjectOutputStream(outputStream);
      out.writeObject(source);

      ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
      ObjectInputStream in = new ObjectInputStream(inputStream);

      Order copy = (Order) in.readObject();
      return copy;
      }
    • 运行结果

      1
      2
      3
      4
      5
      6
      order: com.codecho.copy.Order@330bedb4
      address: com.codecho.copy.Address@2503dbd3
      detailAddress: 凤起路
      copyOrder: com.codecho.copy.Order@7e6cbb7a
      copyAddress: com.codecho.copy.Address@7c3df479
      detailAddress: 凤起路