当前位置: 首页 > news >正文

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

关注:CodingTechWork

引言

  在 Java 开发中,对象的拷贝是一个常见的需求,尤其是在处理复杂数据结构时。深拷贝(Deep Copy)和浅拷贝(Shallow Copy)是两种常见的拷贝方式,它们在实现和效果上有着显著的区别。本文将详细介绍深拷贝和浅拷贝的概念、区别,并通过 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()));}
}

输出:

Original Address: [456 Elm St, Apt 456]
Shallow Copied Address: [456 Elm St, Apt 456]

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

深拷贝示例

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()));}
}

输出:

Original Address: [456 Elm St, Apt 456]
Deep Copied Address: [123 Main St, Apt 456]

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

深拷贝的常用工具类

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

使用 SerializationUtils 实现深拷贝

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

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()));}
}

输出:

Original Address: [456 Elm St, Apt 456]
Deep Copied Address: [123 Main St, Apt 456]

使用 JSON 序列化实现深拷贝

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

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()));}
}

输出:

Original Address: [456 Elm St, Apt 456]
Deep Copied Address: [123 Main St, Apt 456]

自己实现深拷贝工具类

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

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()));}
}

输出:

Original Address: [456 Elm St, Apt 456]
Deep Copied Address: [123 Main St, Apt 456]

总结

概念

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

区别

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

实现方式

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

选择建议

  • 如果对象结构简单,且不需要完全独立的副本,可以选择浅拷贝。
  • 如果需要完全独立的副本,尤其是对象结构复杂时,建议使用深拷贝。
  • 在实际开发中,可以根据具体需求选择合适的实现方式,或者结合多种方式实现更灵活的拷贝逻辑。

相关文章:

  • 数据预处理:前缀和算法详解
  • Vue3 + TypeScript 实现二维码生成与展示
  • poi生成横向文档以及复杂表头
  • 驱动开发硬核特训 · Day 18:深入理解字符设备驱动与子系统的协作机制(以 i.MX8MP 为例)
  • 欧拉计划 Project Euler54(扑克手牌)题解
  • MySQL运算符
  • stack和queue的学习
  • 【实证分析】ESG发展对企业新质生产力影响的研究—来自中国A股上市企业的经验
  • ROS第十二梯:ros-noetic和Anaconda联合使用
  • Python常用的第三方模块之【pymysql库】操作数据库
  • HarmonyOS 5.0应用开发——MVVM模式的应用
  • CF2103F Maximize Nor
  • AI 人工智能模型:从理论到实践的深度解析⚡YQW · Studio ⚡【Deepseek】【Chat GPT】
  • 深度学习中的黑暗角落:梯度消失与梯度爆炸问题解析
  • springboot+vue 支付宝支付(沙箱方式,测试环境使用)
  • VUE Element-ui Message 消息提示组件自定义封装
  • 如何在编译命令中添加灰度标识
  • cnas认证注意事项,cnas认证审核有效期?cnas认证难吗?
  • 思科路由器密码绕过+重置
  • uniapp小程序使用echarts
  • 沈晓萍︱严金清:比斯坦因更早获得敦煌文物的无锡名士
  • 国台办:相关优化离境退税政策适用于来大陆的台湾同胞
  • 三大白电巨头去年净利近900亿元:美的持续领跑,格力营收下滑
  • 央行副行长:我们在研究丰富政策工具箱,将适时推出增量政策
  • IPO周报|4月最后2只新股周一申购,今年以来最低价股来了
  • 中纪报:五一节前公开通报释放强烈信号,以铁律狠刹歪风邪气