深克隆和浅克隆的理解

深拷贝

深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是对象复制的两种不同方式,它们的主要区别在于复制对象时处理对象内部引用的方式:

  • 浅拷贝(Shallow Copy):
  1. 浅拷贝创建一个新对象,新对象的内容与原始对象相同。
  2. 对于基本数据类型的属性,新对象会复制属性的值。
  3. 对于引用类型的属性,新对象会复制属性的引用,这意味着新对象和原始对象仍然引用相同的内部对象。
  4. 修改新对象或原始对象内部的引用类型属性都会影响到另一个对象,因为它们共享相同的内部对象。
  • 深拷贝(Deep Copy):
  1. 深拷贝也创建一个新对象,新对象的内容与原始对象相同。
  2. 与浅拷贝不同,深拷贝会递归复制引用类型属性的内部对象,创建新的对象实例,使得新对象和原始对象之间的引用关系完全独立。
  3. 修改新对象或原始对象内部的引用类型属性不会相互影响,因为它们引用的是不同的内部对象。

概括来说,浅拷贝只复制对象本身以及其直接属性,而不会复制引用类型属性的内部对象。深拷贝则会递归复制对象及其引用类型属性的内部对象,以创建全新的对象层次结构。

深拷贝的方式有多种:

  1. 使用clone()方法:在Java中,一些类实现了Cloneable接口,可以使用clone()方法来创建一个新对象。需要注意的是,clone()方法的默认实现是浅拷贝,如果需要深拷贝,你需要覆盖clone()方法,手动复制引用属性。

  2. 使用序列化和反序列化:将对象序列化为字节数组,然后反序列化为新对象。这种方法通常需要对象和其所有引用属性都实现Serializable接口。这是一种通用的深拷贝方法,但可能会导致性能损失。

  3. 使用第三方库:有一些第三方库可以帮助你实现深拷贝,如Apache Commons Lang库中的SerializationUtils.clone()方法,以及使用Gson或Jackson这样的JSON库将对象转化为JSON字符串,然后再转化为新对象。这些库通常提供了方便的深拷贝工具。

  4. 自定义的拷贝构造函数:为类创建一个自定义的拷贝构造函数,它接受一个原始对象作为参数,并创建一个新对象,将原始对象的属性逐一复制到新对象。这允许你以更有控制的方式进行深拷贝。

  5. 使用BeanUtils或BeanCopier:Apache Commons BeanUtils或Spring的BeanCopier等库提供了对对象属性的复制和映射,这可以用于执行深拷贝。

深拷贝的使用

  1. 使用clone()方法:clone()方法默认是浅拷贝。
package com.copy;

public class Clone2 implements Cloneable{
    int value;
    MyObject myObject;

    public Clone2(int value, MyObject myObject){
        this.value = value;
        this.myObject = myObject;
    }
    // 自定义深拷贝方法
    public Clone2 deepClone() throws CloneNotSupportedException {
        // 调用父类的 clone 方法,实现浅拷贝
        Clone2 cloned = (Clone2) super.clone();
        // 对引用类型的属性,进行单独处理
        cloned.myObject = new MyObject(this.myObject.getValue());
        return cloned;
    }

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

}
class MyObject {
    private int value;
    public MyObject(int value){
        this.value = value;
    }
    public int getValue(){
        return value;
    }
}
class CloneExample{
    public static void main(String[] args) {
        MyObject myObject = new MyObject(1);
        // 浅拷贝
        Clone2 clone2 = new Clone2(2, myObject);
        try {
            // 调用deepClone方法进行深拷贝
            Clone2 newClone = clone2.deepClone();

            // clone2.myObject.getValue() 是指向同一个对象,所以修改 clone2.myObject.getValue() 的值,newClone.myObject.getValue() 的值也会改变
            System.out.println(clone2.myObject.getValue() + "=====" +newClone.myObject.getValue()); // 1=====1
            System.out.println(clone2.value + "--------->" + clone2.myObject);      // 2--------->com.copy.MyObject@3941a79c
            System.out.println(newClone.value + "--------->" + newClone.myObject);  // 2--------->com.copy.MyObject@6d03e736
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}
  1. 使用序列化和反序列化
package com.copy;

import java.io.*;

/**
 * 使用序列化和反序列化实现深拷贝
 */
public class Clone1 implements Serializable {
    String name;

    Clone1 clone;
    public Clone1(String name, Clone1 clone) {
        this.name = name;
        this.clone = clone;
    }

    @Override
    public String toString() {
        return "Clone1{" +
                "name='" + name + '\'' +
                ", clone=" + clone +
                '}';
    }

    public Clone1 deepCopy() {
        try {
            // 将对象序列化为字节数组,因为写入到流中的是二进制数据,所以可以实现深拷贝
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            // ObjectOutputStream 对象输出流,将对象写入到流中
            ObjectOutputStream obs = new ObjectOutputStream(bos);
            // 将对象写入到流中
            obs.writeObject(this);

            // 从流中读取对象
            ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
            // ObjectInputStream 对象输入流,从流中读取对象
            ObjectInputStream ois = new ObjectInputStream(bis);

            // readObject() 方法从流中读取对象
            return (Clone1) ois.readObject();
        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }

    }
}
class Child {
    public static void main(String[] args) {
        Clone1 c1 = new Clone1("张三", new Clone1("son",null));

        Clone1 c2 = c1.deepCopy();
        c2.name = "李四";
        c2.clone.name = "lisi2";

        System.out.println(c1.equals(c2));  // false

        System.out.println(c1);     // Clone1{name='张三', clone=Clone1{name='son', clone=null}}
        System.out.println(c2);     // Clone1{name='李四', clone=Clone1{name='lisi2', clone=null}}
    }
}
  1. 使用Gson或Jackson实现
package com.copy;
import com.google.gson.Gson;
public class GsonCopy {
    public static void main(String[] args) {
        GsonChild gsonChild = new GsonChild("张三", new GsonChild("李四", null));
        // 使用Gson进行深拷贝
        GsonChild gsonChild1 = gsonChild.deepCopy();

        // 修改深拷贝对象的值
        gsonChild1.getChild().setName("王五");
        gsonChild1.setName("老六");

        // 输出原始对象和深拷贝对象
        System.out.println("Original: " + gsonChild.getName() + " - " + gsonChild.getChild().getName());// Original: 张三 - 李四

        System.out.println("Deep Copy: " + gsonChild1.getName() + " - " + gsonChild1.getChild().getName());// Deep Copy: 老六 - 王五
    }
}
class GsonChild{
    private String name;
    private GsonChild child;
    public GsonChild(String name, GsonChild child){
        this.name = name;
        this.child = child;
    }
    // 使用 Gson 实现深拷贝
    public GsonChild deepCopy(){
        // 创建 Gson 对象
        Gson gson = new Gson();
        // 将对象转换为 json 字符串
        String json = gson.toJson(this);
        // 将 json 字符串转换为对象
        return gson.fromJson(json, GsonChild.class);
    }
    public String getName(){
        return name;
    }
    public GsonChild getChild(){
        return child;
    }
    public void setName(String name) {
        this.name = name;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

-今非昔比°

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值