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

Java:对象的浅拷贝与深拷贝

目录

一、概念

二、实现方式

2.1 浅拷贝(不推荐)

2.2 深拷贝

2.2.1 方法一:重写 clone() 方法并递归克隆(常用)

2.2.2 方法二:通过序列化实现(更强大,但更重)

2.2.3 使用拷贝构造方法或拷贝工厂(推荐)


一、概念

浅拷贝 (Shallow Copy):只复制对象本身以及对象中的基本数据类型字段的值,而对于对象中的引用类型字段,则只复制其内存地址(即引用),而不复制引用的对象本身。

影响:原始对象和拷贝对象中的引用字段指向的是同一个堆内存中的子对象。修改其中一个对象的引用字段的内容,另一个对象的对应字段也会“看到”这个变化。

深拷贝 (Deep Copy):仅复制对象本身和基本数据类型字段的值,还会递归地复制所有引用类型字段所指向的对象,直到所有可达的对象都被复制。

影响:原始对象和拷贝对象完全独立,它们所有的引用字段都指向不同的对象。修改其中一个对象的任何内容,都不会影响另一个对象。

二、实现方式

2.1 浅拷贝(不推荐)

实现方式:依靠Object 类的 clone() 方法。

实现条件:

  • 被拷贝的类实现 Cloneable 接口(这是一个标记接口,没有方法)。

  • 被拷贝的类重写 Object 的 clone() 方法,并在其中调用 super.clone()

public class Person implements Cloneable {private String name;        // String (不可变对象,可视为基本类型)private int age;            // 基本类型private Address address;    // 引用类型// 构造方法、getters、setters 省略...@Overridepublic Object clone() throws CloneNotSupportedException {// 直接调用Object的clone()方法,完成基本数据类型和引用地址的复制return super.clone();}
}public class Address {private String city;private String street;// 构造方法、getters、setters...
}public class TestShallowCopy {public static void main(String[] args) throws CloneNotSupportedException {Address addr = new Address("北京", "长安街");Person original = new Person("张三", 25, addr);// 进行浅拷贝Person copied = (Person) original.clone();System.out.println(original == copied);       // false,是两个不同的对象System.out.println(original.getName() == copied.getName()); // true,String池或同一对象(可能)System.out.println(original.getAddress() == copied.getAddress()); // true!关键在这里:引用指向同一个Address对象// 修改拷贝对象的引用类型成员copied.getAddress().setCity("上海");// 原始对象的address也被修改了!System.out.println(original.getAddress().getCity()); // 输出:上海}
}

2.2 深拷贝

2.2.1 方法一:重写 clone() 方法并递归克隆(常用)

// 1、让 Address 类也变得可克隆(实现 Cloneable 并重写 clone())
public class Address implements Cloneable {private String city;private String street;// ... 其他代码 ...@Overridepublic Object clone() throws CloneNotSupportedException {return super.clone();}
}// 2、在 Person 的 clone() 方法中,不仅调用 super.clone(),还要手动克隆 address 字段
public class Person implements Cloneable {private String name;private int age;private Address address;// ... 其他代码 ...@Overridepublic Object clone() throws CloneNotSupportedException {// 1. 先调用super.clone()完成浅拷贝Person copied = (Person) super.clone();// 2. 对引用类型字段,手动调用其clone()方法进行深拷贝copied.address = (Address) this.address.clone();// 3. 返回深拷贝后的对象return copied;}
}// 3、测试
public class TestDeepCopy {public static void main(String[] args) throws CloneNotSupportedException {Address addr = new Address("北京", "长安街");Person original = new Person("张三", 25, addr);Person copied = (Person) original.clone();System.out.println(original.getAddress() == copied.getAddress()); // false!现在是不同的Address对象// 修改拷贝对象的addresscopied.getAddress().setCity("上海");// 原始对象的address不受影响System.out.println(original.getAddress().getCity()); // 输出:北京System.out.println(copied.getAddress().getCity());   // 输出:上海}
}

2.2.2 方法二:通过序列化实现(更强大,但更重)

前提:所有涉及到的类都必须实现 java.io.Serializable 接口。

import java.io.*;public class DeepCopyUtil {@SuppressWarnings("unchecked")public static <T extends Serializable> T deepCopy(T object) {try (ByteArrayOutputStream baos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(baos)) {// 将对象写入字节流oos.writeObject(object);oos.flush();try (ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bais)) {// 从字节流中读出新的对象return (T) ois.readObject();}} catch (IOException | ClassNotFoundException e) {throw new RuntimeException("Deep copy failed", e);}}
}// Person和Address类都需要实现Serializable接口
public class Person implements Serializable { ... }
public class Address implements Serializable { ... }// 使用工具类进行深拷贝
Person original = new Person(...);
Person copied = DeepCopyUtil.deepCopy(original); // 完美的深拷贝

优点:无需关心对象内部复杂的引用结构,序列化机制会自动完成所有递归复制。
缺点:性能开销比 clone() 方法大;所有相关类都必须实现 Serializable 接口。

2.2.3 使用拷贝构造方法或拷贝工厂(推荐)

这是一种非常推荐的方式,它不依赖 Cloneable 接口,代码更清晰,也更灵活。

  • 拷贝构造方法:接受一个同一类型的参数,并据此构造一个新对象。

  • 拷贝工厂:一个静态方法,用于完成拷贝。

public class Person {private String name;private int age;private Address address;// 拷贝构造方法public Person(Person other) {this.name = other.name;this.age = other.age;// 对引用类型,调用其拷贝构造方法进行深拷贝this.address = new Address(other.address); // 假设Address也有拷贝构造方法}// 拷贝工厂 (静态方法)public static Person newInstance(Person other) {return new Person(other);}
}public class Address {private String city;private String street;// Address的拷贝构造方法public Address(Address other) {this.city = other.city;this.street = other.street;}
}// 使用
Person original = new Person(...);
Person copied = new Person(original); // 使用拷贝构造方法
// 或
Person copied2 = Person.newInstance(original); // 使用拷贝工厂

优点:代码意图明确,易于控制和扩展,是《Effective Java》推荐的方式。

http://www.dtcms.com/a/347794.html

相关文章:

  • 获取高德地图经纬度解析地址的免费API接口(无调用限制)
  • JWT实现Token登录验证
  • 任务型Agent:执行计划详细设计
  • 计算机组成原理(11) 第二章 - 存储系统的基本概念
  • Introduction to GIS ——Chapter 1(Introduction)
  • 控制建模matlab练习15:线性状态反馈控制器-④最优化控制LQR
  • 动态内存详解
  • 图、最小生成树与最短路径
  • 算法练习-最长连续序列
  • 最短路径和关键路径的算法
  • Linux学习:信号的保存
  • 【什么是大模型自注意力机制?】
  • 腾讯wxg后台开发面经
  • A Large Scale Synthetic Graph Dataset Generation Framework的学习笔记
  • JavaSpring+mybatis+Lombok,实现java架构[保姆教程]
  • KVM虚拟化:提升企业效率的利器
  • 编程刷题-P1746 离开中山路 BFS/最短路径
  • 数据结构算法:顺序表
  • 电脑零广告快响应提速(一)之卸载搜狗输入法使用RIME—东方仙盟
  • qt ElaWidgetTools第一个实例
  • linux进程调度相关头文件sched.h介绍与使用指南
  • 油猴(tampermonkey)脚本下载及安装使用教程!绿色版
  • [python编程] 零基础入门面向对象
  • Python面向对象高级编程——定制类
  • 本地部署开源书签管理工具 LinkAce 并实现外部访问( Windows 版本)
  • git实战问题(6)git push 时发现分支已被更新,push失败了怎么办
  • EPWpy 安装教程
  • 原初书写与符号嬗变:文字学理论的多维透视与当代重估
  • 【LeetCode】24. 两两交换链表中的节点
  • 青少年机器人技术(五级)等级考试试卷(2021年12月)