Java | 深拷贝与浅拷贝工具类解析和自定义实现

关注:CodingTechWork

引言

在 Java 开发中,对象的拷贝是一个常见的需求,尤其是在处理复杂数据结构时。深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是两种常见的拷贝方式,它们在实现和效果上有着显著的区别。本文将详细介绍深拷贝和浅拷贝的概念、区别,并通过 Java 代码示例进行说明。同时,还会介绍一些常用的深拷贝工具类以及如何自己实现一个深拷贝工具。

深拷贝与浅拷贝的概念

浅拷贝

浅拷贝是指创建一个新对象,然后将当前对象的非静态字段复制到新对象中。如果字段是值类型的(如基本数据类型或不可变对象),那么将复制字段的值;如果字段是引用类型的(如数组、集合、自定义对象等),则复制引用但不复制引用的对象。因此,原始对象和副本对象将引用同一个对象。

深拷贝

深拷贝是指创建一个新对象,然后递归地将当前对象的所有字段(包括值类型和引用类型)复制到新对象中。对于引用类型的字段,深拷贝会创建一个新的对象,并将其复制到副本对象中。因此,原始对象和副本对象是完全独立的,修改一个对象不会影响另一个对象。

深拷贝与浅拷贝的区别

特性 浅拷贝 深拷贝
值类型字段 复制字段的值 复制字段的值
引用类型字段 复制引用,不复制引用的对象 创建新的对象,并复制引用的对象
修改影响 修改原始对象可能影响副本对象 修改原始对象不会影响副本对象
实现复杂度 简单 复杂,需要递归
性能 较快 较慢,尤其是对象结构复杂时

浅拷贝与深拷贝的代码示例

浅拷贝示例

java 复制代码
import java.util.Arrays;

class Person {
    private String name;
    private String[] address;

    public Person(String name, String[] address) {
        this.name = name;
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public String[] getAddress() {
        return address;
    }

    // 浅拷贝方法
    public Person shallowCopy() {
        return new Person(this.name, this.address);
    }
}

public class ShallowCopyDemo {
    public static void main(String[] args) {
        // 创建原始对象
        Person original = new Person("Alice", new String[]{"123 Main St", "Apt 456"});

        // 浅拷贝
        Person shallowCopied = original.shallowCopy();

        // 修改原始对象的地址
        original.getAddress()[0] = "456 Elm St";

        System.out.println("Original Address: " + Arrays.toString(original.getAddress()));
        System.out.println("Shallow Copied Address: " + Arrays.toString(shallowCopied.getAddress()));
    }
}

输出:

less 复制代码
Original Address: [456 Elm St, Apt 456]
Shallow Copied Address: [456 Elm St, Apt 456]

从输出可以看到,修改原始对象的地址也影响了浅拷贝的对象,因为它们共享同一个地址数组。

深拷贝示例

java 复制代码
import java.util.Arrays;

class Person {
    private String name;
    private String[] address;

    public Person(String name, String[] address) {
        this.name = name;
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public String[] getAddress() {
        return address;
    }

    // 深拷贝方法
    public Person deepCopy() {
        // 创建新的地址数组
        String[] newAddress = new String[this.address.length];
        System.arraycopy(this.address, 0, newAddress, 0, this.address.length);

        return new Person(this.name, newAddress);
    }
}

public class DeepCopyDemo {
    public static void main(String[] args) {
        // 创建原始对象
        Person original = new Person("Alice", new String[]{"123 Main St", "Apt 456"});

        // 深拷贝
        Person deepCopied = original.deepCopy();

        // 修改原始对象的地址
        original.getAddress()[0] = "456 Elm St";

        System.out.println("Original Address: " + Arrays.toString(original.getAddress()));
        System.out.println("Deep Copied Address: " + Arrays.toString(deepCopied.getAddress()));
    }
}

输出:

less 复制代码
Original Address: [456 Elm St, Apt 456]
Deep Copied Address: [123 Main St, Apt 456]

从输出可以看到,修改原始对象的地址不会影响深拷贝的对象,因为深拷贝创建了一个全新的地址数组。

深拷贝的常用工具类

在 Java 中,有一些常用的工具类可以帮助实现深拷贝,例如 SerializationUtilsJSON 序列化。

使用 SerializationUtils 实现深拷贝

SerializationUtils 是 Apache Commons Lang 提供的工具类,可以利用序列化机制实现深拷贝。以下是示例代码:

java 复制代码
import org.apache.commons.lang3.SerializationUtils;

import java.io.Serializable;

class Person implements Serializable {
    private String name;
    private String[] address;

    public Person(String name, String[] address) {
        this.name = name;
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public String[] getAddress() {
        return address;
    }
}

public class SerializationUtilsDemo {
    public static void main(String[] args) {
        // 创建原始对象
        Person original = new Person("Alice", new String[]{"123 Main St", "Apt 456"});

        // 使用 SerializationUtils 实现深拷贝
        Person deepCopied = SerializationUtils.clone(original);

        // 修改原始对象的地址
        original.getAddress()[0] = "456 Elm St";

        System.out.println("Original Address: " + Arrays.toString(original.getAddress()));
        System.out.println("Deep Copied Address: " + Arrays.toString(deepCopied.getAddress()));
    }
}

输出:

less 复制代码
Original Address: [456 Elm St, Apt 456]
Deep Copied Address: [123 Main St, Apt 456]

使用 JSON 序列化实现深拷贝

JSON 序列化是一种常见的深拷贝方式,可以利用 GsonJackson 等库实现。以下是使用 Gson 的示例代码:

java 复制代码
import com.google.gson.Gson;

class Person {
    private String name;
    private String[] address;

    public Person(String name, String[] address) {
        this.name = name;
        this.address = address;
    }

    public String getName() {
        return name;
    }

    public String[] getAddress() {
        return address;
    }
}

public class GsonDemo {
    public static void main(String[] args) {
        // 创建原始对象
        Person original = new Person("Alice", new String[]{"123 Main St", "Apt 456"});

        // 使用 Gson 实现深拷贝
        Gson gson = new Gson();
        String json = gson.toJson(original);
        Person deepCopied = gson.fromJson(json, Person.class);

        // 修改原始对象的地址
        original.getAddress()[0] = "456 Elm St";

        System.out.println("Original Address: " + Arrays.toString(original.getAddress()));
        System.out.println("Deep Copied Address: " + Arrays.toString(deepCopied.getAddress()));
    }
}

输出:

less 复制代码
Original Address: [456 Elm St, Apt 456]
Deep Copied Address: [123 Main St, Apt 456]

自己实现深拷贝工具类

如果需要更灵活的深拷贝实现,可以自己编写一个工具类。以下是一个简单的深拷贝工具类的实现:

java 复制代码
import java.util.ArrayList;
import java.util.List;

public class DeepCopyUtils {

    public static <T> T deepCopy(T object) {
        if (object == null) {
            return null;
        }

        // 检查是否是基本数据类型或不可变对象
        if (object instanceof String || object instanceof Number || object instanceof Boolean) {
            return object;
        }

        // 检查是否是数组
        if (object.getClass().isArray()) {
            Object[] array = (Object[]) object;
            Object[] newArray = new Object[array.length];
            for (int i = 0; i < array.length; i++) {
                newArray[i] = deepCopy(array[i]);
            }
            return (T) newArray;
        }

        // 检查是否是集合
        if (object instanceof List) {
            List<Object> list = (List<Object>) object;
            List<Object> newList = new ArrayList<>();
            for (Object item : list) {
                newList.add(deepCopy(item));
            }
            return (T) newList;
        }

        // 如果是自定义对象,需要手动处理
        if (object instanceof Person) {
            Person person = (Person) object;
            return (T) new Person(person.getName(), deepCopy(person.getAddress()));
        }

        throw new IllegalArgumentException("Unsupported type: " + object.getClass().getName());
    }

    private static String[] deepCopy(String[] array) {
        if (array == null) {
            return null;
        }
        String[] newArray = new String[array.length];
        System.arraycopy(array, 0, newArray, 0, array.length);
        return newArray;
    }

    public static void main(String[] args) {
        // 创建原始对象
        Person original = new Person("Alice", new String[]{"123 Main St", "Apt 456"});

        // 使用自定义工具类实现深拷贝
        Person deepCopied = DeepCopyUtils.deepCopy(original);

        // 修改原始对象的地址
        original.getAddress()[0] = "456 Elm St";

        System.out.println("Original Address: " + Arrays.toString(original.getAddress()));
        System.out.println("Deep Copied Address: " + Arrays.toString(deepCopied.getAddress()));
    }
}

输出:

less 复制代码
Original Address: [456 Elm St, Apt 456]
Deep Copied Address: [123 Main St, Apt 456]

总结

概念

  • 浅拷贝:只复制对象的直接字段,对于引用类型字段,复制的是引用而不是对象本身。
  • 深拷贝:递归复制对象的所有字段,包括引用类型字段所指向的对象。

区别

  • 浅拷贝创建的对象与原始对象可能共享引用类型字段所指向的对象,而深拷贝创建的对象与原始对象完全独立。
  • 深拷贝的实现更复杂,性能开销更大,但可以保证对象的完全独立性。

实现方式

  • 浅拷贝 :可以通过 Object.clone() 方法或手动实现。
  • 深拷贝 :可以通过手动递归实现、利用序列化机制(如 SerializationUtils)、JSON 序列化(如 GsonJackson)或自定义工具类实现。

选择建议

  • 如果对象结构简单,且不需要完全独立的副本,可以选择浅拷贝。
  • 如果需要完全独立的副本,尤其是对象结构复杂时,建议使用深拷贝。
  • 在实际开发中,可以根据具体需求选择合适的实现方式,或者结合多种方式实现更灵活的拷贝逻辑。
相关推荐
uzong3 小时前
技术故障复盘模版
后端
GetcharZp4 小时前
基于 Dify + 通义千问的多模态大模型 搭建发票识别 Agent
后端·llm·agent
桦说编程4 小时前
Java 中如何创建不可变类型
java·后端·函数式编程
IT毕设实战小研4 小时前
基于Spring Boot 4s店车辆管理系统 租车管理系统 停车位管理系统 智慧车辆管理系统
java·开发语言·spring boot·后端·spring·毕业设计·课程设计
wyiyiyi5 小时前
【Web后端】Django、flask及其场景——以构建系统原型为例
前端·数据库·后端·python·django·flask
阿华的代码王国6 小时前
【Android】RecyclerView复用CheckBox的异常状态
android·xml·java·前端·后端
Jimmy6 小时前
AI 代理是什么,其有助于我们实现更智能编程
前端·后端·ai编程
AntBlack6 小时前
不当韭菜V1.1 :增强能力 ,辅助构建自己的交易规则
后端·python·pyqt
bobz9657 小时前
pip install 已经不再安全
后端
寻月隐君7 小时前
硬核实战:从零到一,用 Rust 和 Axum 构建高性能聊天服务后端
后端·rust·github