简介

  • 什么拷贝:在Java程序中,有时候会遇到对象的复制,也就是需要数据一致的另外一个对象,这就是需要用到拷贝。
  • 创建对象的方式:
    • new关键字:这是最常用的一种方式,通过 new 关键字调用类的有参或无参构造方法来创建对象。比如 Object obj = new Object();
    • Class 类的 newInstance() 方法:默认是调用类的无参构造方法创建对象。比如 Person p2 = (Person) Class.forName(“cn.icanci.test.Person”).newInstance();
    • Constructor 类的 newInstance 方法:通过 java.lang.relect.Constructor 类的 newInstance() 方法指定某个构造器来创建对象。Person p3 = (Person) Person.class.getConstructors()[0].newInstance();第二种方法利用 Class 的 newInstance() 方法创建对象,其内部调用还是 Constructor 的 newInstance() 方法。
    • 克隆:Clone 是 Object 类中的一个方法,通过 对象A.clone() 方法会创建一个内容和对象 A 一模一样的对象 B,clone 克隆,顾名思义就是创建一个一模一样的对象出来。Person p4 = (Person) p3.clone();
    • 反序列化:序列化是把堆内存中的 Java 对象数据,通过某种方式把对象存储到磁盘文件中或者传递给其他网络节点(在网络上传输)。而反序列化则是把磁盘文件中的对象数据或者把网络节点上的对象数据,恢复成Java对象模型的过程
  • 而我们这片讲的深拷贝和浅拷贝就和克隆有关

    clone方法

    1. // 位于Object
    2. protected native Object clone() throws CloneNotSupportedException;

    浅拷贝案例

    ```java @Data public class Person implements Cloneable { public String name; public int page; public Address address;

    @Override protected Object clone() throws CloneNotSupportedException {

    1. return super.clone();

    } }

```java
@Data
public class Address {
    private String address;
    private String city;
}
public class ObjectCopyTest {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        p1.setName("icanci");
        p1.setPage(1);
        Address address = new Address();
        address.setCity("湖北");
        address.setAddress("武汉市");
        p1.setAddress(address);
        Person p2 = (Person) p1.clone();
        System.out.println(p1);
        System.out.println(p2);
        System.out.println(p1.getAddress());
        System.out.println(p2.getAddress());
    }
}
// 执行结果
// com.baselearn.objectcopy.Person@2a139a55
// com.baselearn.objectcopy.Person@15db9742
// com.baselearn.objectcopy.Address@6d06d69c
// com.baselearn.objectcopy.Address@6d06d69c
  • 从执行结果看出,p1 和 p2 的内存地址不一样,是新的对象,但是各自的Address属性是一样的,这样就说明了默认实现的拷贝,仅仅是拷贝了数据的引用地址,而没有再去创建对象进行赋值。
  • 那么这样问题就来了,我需要是不同的对象,如果我修改了p1的address的city,那么p2的address的city也是我修改之后的内容。这就不符合我的预期了
  • 所以这样称为浅拷贝
  • 此处不画内存示意图,在之前的面向对象有类似的图

    如果实现深拷贝

    方式:覆写clone方法内容

    @Getter
    @Setter
    public class PersonClone implements Cloneable {
      public String  name;
      public int     page;
      public Address address;
    
      @Override
      protected Object clone() throws CloneNotSupportedException {
          PersonClone personClone = new PersonClone();
          personClone.setName(this.name);
          personClone.setPage(this.page);
          if (this.address != null) {
              Address address = new Address();
              address.setAddress(address.getAddress());
              address.setCity(address.getCity());
              personClone.setAddress(address);
          }
          return personClone;
      }
    }
    

    ```java public class ObjectCopyTest { public static void main(String[] args) throws Exception {

      PersonClone p1 = new PersonClone();
      p1.setName("icanci");
      p1.setPage(1);
      Address address = new Address();
      address.setCity("湖北");
      address.setAddress("武汉市");
      p1.setAddress(address);
      PersonClone p2 = (PersonClone) p1.clone();
      System.out.println(p1);
      System.out.println(p2);
      System.out.println(p1.getAddress());
      System.out.println(p2.getAddress());
    

    } }

// com.baselearn.objectcopy.PersonClone@2a139a55 // com.baselearn.objectcopy.PersonClone@15db9742 // com.baselearn.objectcopy.Address@6d06d69c // com.baselearn.objectcopy.Address@7852e922

<a name="FmaFs"></a>
## 方式:每个引用对象内部都实现克隆方法
```java
@Getter
@Setter
public class Person implements Cloneable {
    public String  name;
    public int     page;
    public Address address;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person p = (Person) super.clone();
        p.address = (Address) address.clone();
        return p;
    }
}
@Getter
@Setter
public class Address implements Cloneable {
    private String address;
    private String city;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
public class ObjectCopyTest {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        p1.setName("icanci");
        p1.setPage(1);
        Address address = new Address();
        address.setCity("湖北");
        address.setAddress("武汉市");
        p1.setAddress(address);
        Person p2 = (Person) p1.clone();
        System.out.println(p1);
        System.out.println(p2);
        System.out.println(p1.getAddress());
        System.out.println(p2.getAddress());
    }
}

// com.baselearn.objectcopy.Person@2a139a55
// com.baselearn.objectcopy.Person@15db9742
// com.baselearn.objectcopy.Address@6d06d69c
// com.baselearn.objectcopy.Address@7852e922

方式:序列化和反序列化

@UtilityClass
public class CloneUtils {
    /**
     * 对象拷贝
     *
     * @param obj 需要拷贝的对象
     * @param <T> 泛型
     * @return 返回拷贝的对象
     */
    @SuppressWarnings("unchecked")
    public <T extends Serializable> T clone(T obj) {
        T cloneObj = null;
        try {
            // 有先后顺序的 必须先写,在创建对象的时候没有写,会导致读不出来
            ByteArrayOutputStream baos = new ByteArrayOutputStream(); //
            ObjectOutputStream oos = new ObjectOutputStream(baos); //
            oos.writeObject(obj);
            oos.close();

            // 分配内存空间,写入原始对象,生成新对象
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); //
            ObjectInputStream ois = new ObjectInputStream(bais);

            cloneObj = (T) ois.readObject();
            ois.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return cloneObj;
    }
}
@Getter
@Setter
public class Person implements Serializable {
    public String  name;
    public int     page;
    public Address address;
}
@Getter
@Setter
public class Address implements Serializable {
    private String address;
    private String city;
}
public class ObjectCopyTest {
    public static void main(String[] args) throws Exception {
        Person p1 = new Person();
        p1.setName("icanci");
        p1.setPage(1);
        Address address = new Address();
        address.setCity("湖北");
        address.setAddress("武汉市");
        p1.setAddress(address);
        Person p2 = CloneUtils.clone(p1);
        System.out.println(p1);
        System.out.println(p2);
        System.out.println(p1.getAddress());
        System.out.println(p2.getAddress());
    }
}
// com.baselearn.objectcopy.Person@5e481248
// com.baselearn.objectcopy.Person@312b1dae
// com.baselearn.objectcopy.Address@66d3c617
// com.baselearn.objectcopy.Address@7530d0a
  • 需要每个引用对象都实现Serializable接口,才能使用序列化的方式处理

    总结

  • 基本上不会使用克隆方法,而是使用自定义的Mapper去转换,后续在项目的应用中,回去学习相应的工具包。