java拷贝技术探究


浅拷贝和深拷贝的区别

浅拷贝: 对当前对象进行克隆,并克隆该对象所包含的8种基本数据类型和String类型的属性(对当前对象拷贝一份并重新分配内存,即产生了新的对象)。但如果被克隆的对象中包含除了8种数据类型和String类型外的其他类型的属性,浅拷贝并不会克隆这些属性(即不会为这些属性分配内存,而是引用原来对象中的属性)

深拷贝: 深拷贝是在浅拷贝的基础上,递归地克隆除了8种基本类型和String类型之外的属性(即为这些属性重新分配内存而非引用原来对象中的属性)

浅拷贝(Shallow Clone)

构建实体类
package com.nicholas.example.model;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

import java.io.Serializable;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

/**
 * @author Nicholas
 */
@Getter
@Setter
@ToString
public class User implements Cloneable, Serializable {

    /**
     * 编号
     */
    private int id;

    /**
     * 名称
     */
    private String name;

    /**
     * 属性信息
     */
    private Map<String,Object> props;

    @Override
    public Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}
浅拷贝测试
package com.nicholas.example.test;

import java.util.HashMap;
import java.util.Map;

public class CloningTest {

    public static void main(String[] args) throws CloneNotSupportedException {
        User origin = new User();
        origin.setId(1);
        origin.setName("Nicholas");
        Map<String, Object> props = new HashMap<>(2);
        props.put("salary",10000);
        props.put("city","NanJing");
        origin.setProps(props);

        User cloneUser = (User) origin.clone();

        // 检查user和cloneUser的属性是否一致
        System.out.println("user: " + origin);
        System.out.println("cloneUser: " + cloneUser);
        System.out.println("user and cloneUser === test: " + (origin == cloneUser));

        // 检查user和cloneUser的props是否一致
        System.out.println("user props: " + origin.getProps());
        System.out.println("cloneUser props: " + cloneUser.getProps());
        System.out.println("user and cloneUser props === test: " + (origin.getProps() == cloneUser.getProps()));

        // 让我们看看对默认克隆的影响
        // 修改user对象的props
        origin.getProps().put("title","CTO");
        origin.getProps().put("city","ChangZhou");
        System.out.println("cloneUser props:" + cloneUser.getProps());

        // 修改user的name
        origin.setName("SunChunYang");
        System.out.println("cloneUser name:" + cloneUser.getName());
    }
}
结果输出
D:\Develop\env\java\jdk-11.0.2\bin\java.exe ...
com.example.demo.model.CloningTest
user: User(id=1, name=Nicholas, props={salary=10000, city=NanJing})
cloneUser: User(id=1, name=Nicholas, props={salary=10000, city=NanJing})
user and cloneUser === test: false
user props: {salary=10000, city=NanJing}
cloneUser props: {salary=10000, city=NanJing}
user and cloneUser props === test: true
cloneUser props:{salary=10000, title=CTO, city=ChangZhou}
cloneUser name:Nicholas

Process finished with exit code 0

深拷贝(Deep Clone)

构建实体类
/**
 * @author Nicholas
 */
@Getter
@Setter
@ToString
public class User implements Cloneable, Serializable {

    /**
     * 编号
     */
    private int id;

    /**
     * 名称
     */
    private String name;

    /**
     * 属性信息
     */
    private Map<String,Object> props;

    /**
     * 方法一:最原始的实现方式,经过构造方法手建立
     * 优势:
     * 1.实现简单直观
     * 2.不须要依赖额外的接口和第三方包
     * 缺点:
     * 1.成员变量发生变更须要修改方法,不知足开闭原则;
     * 2.不具备可复用性;
     */
    @Override
    public Object clone() {
       User copyUser = new User();
        copyUser.setId(this.getId());
        copyUser.setName(this.getName());
        if (this.getProps() != null) {
            Map<String, Object> hm = new HashMap<>(2);
            String key;
            Iterator<String> it = this.props.keySet().iterator();
            // Deep Copy of field by field
            while (it.hasNext()) {
                key = it.next();
                hm.put(key, this.props.get(key));
            }
            copyUser.setProps(hm);
        }
        return copyUser;
    }
    
    /**
     * 方法二:使用Object的clone方法实现
     * 优势:
     * 1.较方式1实现更简单,不须要关注copy细节;
     * 2.不须要依赖第三方包;
     * 3.不修改引用类型成员变量不须要修改代码
     * 缺点:
     * 1.须要实现Cloneable,重写父类clone方法,不知足里式替换;
     * 2.且引用类型成员变量发生变更须要修改方法,不知足开闭原则;
     * 3.不具备可复用性;
     */
    public User clone() throws CloneNotSupportedException {
        User cloneUser = (User) this.clone();
        if (this.getProps() != null) {
            Map<String, Object> hm = new HashMap<>(2);
            String key;
            Iterator<String> it = this.props.keySet().iterator();
            // Deep Copy of field by field
            while (it.hasNext()) {
                key = it.next();
                hm.put(key, this.props.get(key));
            }
            cloneUser.setProps(hm);
        }
        return cloneUser;
    }

    /**
     * 方法三:使用Java自带的流方式实现
     * 优势:
     * 1.不破坏类的封装,无需了解被copy对象的内部
     * 2.不须要依赖第三方包
     * 3.代码可复用
     * 缺点:
     * 1.须要实现Serializable接口,会有额外的开销
     */
    public User clone() throws IOException, ClassNotFoundException {
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this);
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (User)ois.readObject();
    }

    /**
     * 方法四:使用第三方包Jackson实现
     * 优势:
     * 1.不破坏类的封装,无需了解被copy对象的内部
     * 2.不须要实现接口
     * 3.代码可复用
     * 缺点:
     * 1.须要依赖第三方包
     * 2.内部实现复杂
     */
    public User clone() throws JsonProcessingException {
        ObjectMapper objectMapper = new ObjectMapper();
        return objectMapper.readValue(objectMapper.writeValueAsString(this), User.class);
    }
}
深拷贝测试
package com.nicholas.example.test;

import java.util.HashMap;
import java.util.Map;

public class CloningTest {

    public static void main(String[] args) throws CloneNotSupportedException {
        User origin = new User();
        origin.setId(1);
        origin.setName("Nicholas");
        Map<String, Object> props = new HashMap<>(2);
        props.put("salary",10000);
        props.put("city","NanJing");
        origin.setProps(props);

        User cloneUser = (User) origin.clone();

        // 检查user和cloneUser的属性是否一致
        System.out.println("user: " + origin);
        System.out.println("cloneUser: " + cloneUser);
        System.out.println("user and cloneUser === test: " + (origin == cloneUser));

        // 检查user和cloneUser的props是否一致
        System.out.println("user props: " + origin.getProps());
        System.out.println("cloneUser props: " + cloneUser.getProps());
        System.out.println("user and cloneUser props === test: " + (origin.getProps() == cloneUser.getProps()));

        // 让我们看看对默认克隆的影响
        // 修改user对象的props
        origin.getProps().put("title","CTO");
        origin.getProps().put("city","ChangZhou");
        System.out.println("cloneUser props:" + cloneUser.getProps());

        // 修改user的name
        origin.setName("SunChunYang");
        System.out.println("cloneUser name:" + cloneUser.getName());
    }
}
结果输出
D:\Develop\env\java\jdk-11.0.2\bin\java.exe ...
user: User(id=1, name=Nicholas, props={salary=10000, city=NanJing})
cloneUser: User(id=1, name=Nicholas, props={city=NanJing, salary=10000})
user and cloneUser === test: false
user props: {salary=10000, city=NanJing}
cloneUser props: {city=NanJing, salary=10000}
user and cloneUser props === test: false
cloneUser props:{city=NanJing, salary=10000}
cloneUser name:Nicholas

Process finished with exit code 0

文章作者: 会编程的吕洞宾
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 会编程的吕洞宾 !
  目录