深拷贝在java中意味着新对象及其引用的可变对象都是独立副本。1. 通过序列化与反序列化实现深拷贝,适用于复杂对象图,使用简单但性能开销大且需实现serializable接口;2. 手动递归拷贝通过拷贝构造器或deepcopy方法实现,灵活可控但代码冗余、易出错,适合不可序列化或性能敏感场景。两种方式各有优劣,根据实际需求选择。
在Java中,实现对象的深拷贝,核心在于确保新创建的对象不仅拥有与原对象相同的值,更重要的是,它所引用的所有可变对象(比如集合、自定义对象等)也都是全新的独立副本,而非共享引用。这意味着对新对象的任何修改,都不会影响到原对象及其内部结构,反之亦然。实现深拷贝的常见方案主要有:通过序列化与反序列化、手动递归拷贝(包括使用拷贝构造器或克隆方法)、以及利用某些第三方库。
实现Java对象的深拷贝,我个人比较倾向于两种主要策略,它们各有侧重,具体选择看场景和对象的复杂程度。
1. 基于序列化与反序列化
立即学习“Java免费学习笔记(深入)”;
这是实现深拷贝的一种相对“懒人”且强大的方式,尤其适合处理复杂对象图。原理很简单:将对象写入一个输出流(比如ByteArrayOutputStream),再从输入流(ByteArrayInputStream)中读出来。这样“走一圈”下来,你得到的就是一个全新的、完全独立的深拷贝对象。
import java.io.*; public class DeepCopyUtil { @SuppressWarnings("unchecked") public static <T extends Serializable> T deepCopy(T obj) { try { // 写入字节流 ByteArrayOutputStream bos = new ByteArrayOutputStream(); ObjectOutputStream oos = new ObjectOutputStream(bos); oos.writeObject(obj); // 从字节流中读取 ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray()); ObjectInputStream ois = new ObjectInputStream(bis); return (T) ois.readObject(); } catch (IOException | ClassNotFoundException e) { // 实际项目中可能需要更细致的异常处理 System.err.println("深拷贝失败: " + e.getMessage()); throw new RuntimeException("深拷贝操作失败", e); } } // 示例类 public static class Address implements Serializable { private static final long serialVersionUID = 1L; private String city; private String street; public Address(String city, String street) { this.city = city; this.street = street; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } @Override public String toString() { return "Address{" + "city='" + city + '\'' + ", street='" + street + '\'' + '}'; } } public static class Person implements Serializable { private static final long serialVersionUID = 1L; private String name; private int age; private Address address; // 引用类型 public Person(String name, int age, Address address) { this.name = name; this.age = age; this.address = address; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public Address getAddress() { return address; } public void setAddress(Address address) { this.address = address; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + ", address=" + address + '}'; } } public static void main(String[] args) { Address originalAddress = new Address("北京", "朝阳区"); Person originalPerson = new Person("张三", 30, originalAddress); System.out.println("原始对象: " + originalPerson); // 执行深拷贝 Person copiedPerson = DeepCopyUtil.deepCopy(originalPerson); System.out.println("拷贝对象: " + copiedPerson); // 修改拷贝对象的内部引用 copiedPerson.getAddress().setStreet("海淀区"); copiedPerson.setName("李四"); System.out.println("修改后原始对象: " + originalPerson); System.out.println("修改后拷贝对象: " + copiedPerson); // 验证是否是深拷贝 System.out.println("原始地址对象和拷贝地址对象是否是同一个引用: " + (originalPerson.getAddress() == copiedPerson.getAddress())); } }
运行上述代码,你会发现修改copiedPerson的地址后,originalPerson的地址并没有改变,这正是深拷贝的魅力。
2. 手动递归拷贝(拷贝构造器/克隆方法)
这种方式需要你亲自去实现拷贝逻辑。对于每个包含引用类型字段的类,你都需要编写代码来创建这些引用类型字段的新实例,并复制它们的内容。
// 假设有上面的Address类,但它不再需要实现Serializable // public class Address { ... } public class PersonWithManualCopy { private String name; private int age; private DeepCopyUtil.Address address; // 使用上面的Address类 public PersonWithManualCopy(String name, int age, DeepCopyUtil.Address address) { this.name = name; this.age = age; this.address = address; } // 拷贝构造器实现深拷贝 public PersonWithManualCopy(PersonWithManualCopy other) { this.name = other.name; // String是不可变的,直接赋值即可 this.age = other.age; // 基本类型直接赋值 // 对于可变引用类型,必须创建新实例并拷贝其内容 this.address = new DeepCopyUtil.Address(other.address.getCity(), other.address.getStreet()); } // 或者提供一个深拷贝方法 public PersonWithManualCopy deepCopy() { return new PersonWithManualCopy(this.name, this.age, new DeepCopyUtil.Address(this.address.getCity(), this.address.getStreet())); } // Getters and Setters (略) public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public DeepCopyUtil.Address getAddress() { return address; } public void setAddress(DeepCopyUtil.Address address) { this.address = address; } @Override public String toString() { return "PersonWithManualCopy{" + "name='" + name + '\'' + ", age=" + age + ", address=" + address + '}'; } public static void main(String[] args) { DeepCopyUtil.Address originalAddress = new DeepCopyUtil.Address("上海", "浦东新区"); PersonWithManualCopy originalPerson = new PersonWithManualCopy("王五", 25, originalAddress); System.out.println("原始对象: " + originalPerson); // 使用拷贝构造器 PersonWithManualCopy copiedPerson = new PersonWithManualCopy(originalPerson); // 或者使用deepCopy方法 // PersonWithManualCopy copiedPerson = originalPerson.deepCopy(); System.out.println("拷贝对象: " + copiedPerson); copiedPerson.getAddress().setStreet("闵行区"); copiedPerson.setName("赵六"); System.out.println("修改后原始对象: " + originalPerson); System.out.println("修改后拷贝对象: " + copiedPerson); System.out.println("原始地址对象和拷贝地址对象是否是同一个引用: " + (originalPerson.getAddress() == copiedPerson.getAddress())); } }
手动拷贝的好处是你可以完全控制拷贝过程,不需要依赖Serializable接口。但缺点也很明显,如果对象结构复杂,嵌套层级深,工作量会非常大,而且容易出错。
浅拷贝,顾名思义,只是复制了对象本身以及它所包含的基本类型字段的值。对于引用类型字段,它复制的仅仅是这些字段的引用地址,而不是它们指向的实际对象。这就像你复印了一份文件,但文件里提到的“那本书”你只是记下了书名,并没有真的去买一本新的书。
举个例子,假设你有一个订单对象,它内部有一个商品列表(List
简单来说,当你的对象内部包含可变引用类型(比如List、Map、自定义对象等),并且你希望新对象与原对象完全独立,互不影响时,浅拷贝就无法满足需求了。你可能需要修改拷贝后的对象,但又不希望这些修改影响到原始对象的状态,这时候深拷贝就成了必需品。
使用序列化来实现深拷贝,我个人觉得它在很多情况下都是一个非常便捷且可靠的方案。
优点:
缺点:
适用场景:
手动实现深拷贝虽然灵活,但挑战不小,尤其当对象结构变得复杂时。
挑战:
最佳实践:
// 示例:PersonWithManualCopy(PersonWithManualCopy other)
// 示例:public PersonWithManualCopy deepCopy()
适用场景:
以上就是Java对象转换的深拷贝实现方案的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号