深拷贝和浅拷贝区别

在 Java 中,**深拷贝(Deep Copy)浅拷贝(Shallow Copy)**是指对象复制时,复制的方式和结果的不同。理解这两者的区别非常重要,特别是在处理复杂对象时。浅拷贝和深拷贝的主要区别在于对象内部引用的处理方式。

1. 浅拷贝(Shallow Copy)

浅拷贝是指复制对象时,只复制对象本身,而不会复制对象内部引用的其他对象。也就是说,源对象和目标对象内部的引用类型变量(如数组、对象等)指向相同的内存地址(即它们引用的是相同的对象)。因此,源对象和目标对象共享同一份数据。

浅拷贝的特点:

  • 复制的是对象的“引用”,而非对象所指向的数据。
  • 对象的引用类型字段仍然指向原始对象的内存地址(即浅拷贝并没有创建新的引用对象)。

浅拷贝的代码示例:

class Address {
    String city;

    Address(String city) {
        this.city = city;
    }
}

class Person {
    String name;
    Address address;

    Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    // 浅拷贝方法
    public Person shallowCopy() {
        return new Person(this.name, this.address); // 复制的是引用,address 地址相同
    }
}

public class ShallowCopyExample {
    public static void main(String[] args) {
        Address address = new Address("New York");
        Person person1 = new Person("Alice", address);

        // 浅拷贝
        Person person2 = person1.shallowCopy();

        System.out.println(person1.name);        // 输出: Alice
        System.out.println(person2.name);        // 输出: Alice
        System.out.println(person1.address.city); // 输出: New York
        System.out.println(person2.address.city); // 输出: New York

        // 修改 person2 的 address
        person2.address.city = "Los Angeles";
        
        // 由于浅拷贝,person1 和 person2 的 address 是同一个对象
        System.out.println(person1.address.city); // 输出: Los Angeles
        System.out.println(person2.address.city); // 输出: Los Angeles
    }
}

输出结果

Alice
Alice
New York
New York
Los Angeles
Los Angeles

说明

  • person1person2address 引用指向相同的 Address 对象,因此修改 person2address 会影响 person1address
  • 这是因为我们进行了浅拷贝,address 字段只是引用了相同的内存地址。

2. 深拷贝(Deep Copy)

深拷贝是指复制对象时,不仅复制对象本身,而且复制对象内部引用的其他对象。也就是说,深拷贝会创建对象及其所引用的所有对象的副本,源对象和目标对象之间完全独立,它们不会共享任何引用对象。

深拷贝的特点:

  • 复制的是对象及其所有引用的对象,确保新对象的所有引用都指向新创建的对象。
  • 目标对象与源对象在内存中完全独立,修改目标对象的属性不会影响源对象。

深拷贝的代码示例:

class Address {
    String city;

    Address(String city) {
        this.city = city;
    }

    // 深拷贝方法
    public Address deepCopy() {
        return new Address(this.city);  // 创建一个新的 Address 对象
    }
}

class Person {
    String name;
    Address address;

    Person(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    // 深拷贝方法
    public Person deepCopy() {
        // 创建新的 Person 对象,并对 address 进行深拷贝
        return new Person(this.name, this.address.deepCopy());
    }
}

public class DeepCopyExample {
    public static void main(String[] args) {
        Address address = new Address("New York");
        Person person1 = new Person("Alice", address);

        // 深拷贝
        Person person2 = person1.deepCopy();

        System.out.println(person1.name);        // 输出: Alice
        System.out.println(person2.name);        // 输出: Alice
        System.out.println(person1.address.city); // 输出: New York
        System.out.println(person2.address.city); // 输出: New York

        // 修改 person2 的 address
        person2.address.city = "Los Angeles";
        
        // 由于深拷贝,person1 和 person2 的 address 是不同的对象
        System.out.println(person1.address.city); // 输出: New York
        System.out.println(person2.address.city); // 输出: Los Angeles
    }
}

输出结果

Alice
Alice
New York
New York
New York
Los Angeles

说明

  • person1person2address 引用指向不同的对象,person2 修改 address 不会影响 person1
  • 这是因为我们进行了深拷贝,address 字段指向了新创建的 Address 对象。

3. 深拷贝与浅拷贝的对比

特性 浅拷贝(Shallow Copy)深拷贝(Deep Copy)
引用对象的处理 只复制对象本身,不复制对象引用的其他对象。 复制对象本身及其所有引用的对象,确保每个引用指向新的对象。
对象之间的关系 源对象和目标对象共享相同的引用对象。 源对象和目标对象独立,互不影响。
修改影响 修改目标对象会影响源对象,因为它们共享同一引用对象。 修改目标对象不会影响源对象,因为它们不共享任何引用对象。
性能 浅拷贝比深拷贝更高效,因为只涉及对象本身和引用的复制。 深拷贝需要复制整个对象图,性能开销较大。

4. 深拷贝和浅拷贝的实际应用

  • 浅拷贝

    • 适用于不修改对象内部引用的情况下。比如,你只需要复制对象的基本数据,且不关心对象引用类型字段的变化。
    • 常见场景:复制对象用于某些不改变内部引用的操作,如在多线程中共享不可变的对象。
  • 深拷贝

    • 适用于需要复制对象及其引用对象,且这些引用对象的状态不应该被共享的场景。例如,修改一个对象的内部引用数据时不希望影响到其他对象。
    • 常见场景:当对象中包含嵌套的可变对象时,深拷贝能确保每个对象都被独立复制,防止修改一个对象影响到其他对象。
Java碎碎念 文章被收录于专栏

来一杯咖啡,聊聊Java的碎碎念呀

全部评论

相关推荐

### 3.28一面面试官提前加了微信,说分笔试和面试,让我提前准备,说手撕很简单不用担心,就放下心来了,结果一上来就是场景题,让写一个多线程读写安全的环形缓冲区,因为没写过多线程锁,有点懵另外一道是给一个数字字符串,判断需要多少次删除操作,才能使其被25整除(但是一开始想岔去dp了,卡了很久,说明有时候真的不要把题目想得太难,抛开算法反而更好做orz)前面两道题一共花了半小时,因为卡壳了很紧张,我还以为会被直接挂了,没想到面试官并不问题目的思路,而是直接开始问项目了:项目的背景项目的架构追问了一下项目的细节是怎么实现的进程的动态加载内存的动态管理进程和线程的区别多个线程之间哪些资源是共享的介绍一下另外一个项目,也是把背景和架构以及我的分工讲了一遍,面试官就没什么问题了### 反问环节1. **部门的业务主要是什么?** C语言写数据库内核的2. **工作强度怎么样?** 早上九点半到晚上6点,一般不超9点,不强制加班,但有时候项目上线可能要熬夜3. **部门对实习生是怎么培养的?** 每个人会分配一个师兄带,不会的东西都可以问4. **转正率怎么样:** 转正率很高,基本上都可以转正5. **面试表现以及技术栈的建议:** 只要能说清楚项目做的是什么就可以了;简历上没有涉及数据库,但由于是部门把简历要过去的,不能怪你2333### 4.7 二面**项目拷打**两个项目的背景/结构,负责的部分是哪些**操作系统,计网,数据各出了一个八股**程序访问虚拟地址的时候发生了什么?tcp和udp了解数据库吗?数据库崩溃的时候是怎么回滚的?**业务介绍****手头有哪些offer/流程?**倾向于选择哪个?**反问环节**对实习生的培养规划能否提前实习后续还有几轮面试工作强度**手撕**二叉树后序遍历,非递归方式实现最长严格递增子序列(dp做的,面试官问有没有更好的做法,用贪心+一个辅助数组维持最小递增子序列实现了)### 4.11 hr面##### 1. 三跨考研,是如何选择专业和学校的?##### 2. 本科的时候有没有接触过计算机课程?##### 3. 你是专硕还是学硕,毕业对论文/专利有没有要求?##### 4. 分享一下你科研或者学习过程中的一些经历或者挑战##### 5. 详细讲一下科研项目##### 6. 你刚刚说到了和师兄分工合作,那你觉得你核心贡献是什么?负责哪个部分?项目结果满意吗?##### 7. 另外一个项目看起来更偏工程一些,能不能介绍一下项目背景/扮演角色/参加的契机?##### 8. 为什么对计算机底层感兴趣?现在大家都往应用层或者大模型相关发展?##### 9. 现在手头还有那些offer或者流程呢##### 10. 实习地点的选择##### 11. 除了学习之外,你还有什么兴趣爱好?##### 12. 你觉得你压力比较大的时候是什么?##### 13. 在决定未来方向的时候受到了哪些因素影响?怎么去重整自己的经历##### 14. 很多同学觉得考研压力大,但是你觉得决定考研前压力更大,这是为什么呢?##### 15. 实习的时间大概是从什么时候到什么时候,你个人计划实习多久#### 反问环节1.阿里云的培养模式2.部门的业务是什么3.面试多久会有结果呢 **(一周内,面试完会和前面的面试官沟通)**### 4.16官网流程挂,问了hr说相比起来其他同学更匹配,应该就是被横向对比了原本感觉面试表现和体验都不错
查看28道真题和解析 实习进度记录
点赞 评论 收藏
分享
05-12 22:45
已编辑
2222 安卓
55min问了40多个问题,底裤都被扒完了...问题:1,为什么使用flutter2,flutter的优点是什么3,mvvm架构的特点4,flutter和原生混合开发时,页面路由如何管理5,hashmap数据结构如何实现6,红黑树特点,插入和查找的时间复杂度7,hashmap为什么在链表长度为8时转化为红黑树8,内存泄漏的根本原因是什么?9,内存泄漏检测工具的原理了解吗?内存泄漏是如何被检测出来的?10,引用类型有哪些?11,Java中加锁有几种方式?12,审查代码时,如何发现潜在的死锁问题?13,死锁如何产生14,如何避免死锁15,tcp,udp是哪一层的协议,他们的区别是什么?16,tcp如何保证可靠性17,请求头中有哪些信息18,cookie的概念19,序列化和反序列化20,json和protobuf的区别21,数据加密有了解过吗?22,https相比较与http增加了哪些内容?23,https中的证书的概念24,效率比较高的排序算法,哪种算法时间最稳定?25,在实际项目中更轻倾向于自己实现排序算法,还是使用系统提供的方法26,java或c++中排序算法的源码有了解过吗,动态调节排序算法的策略?27,字符串查找算法,kmp时间复杂度,kmp思想是什么?28,计算机两数相乘溢出,如何解决?29,两个链表相交,如何求焦点30,安卓跨进程通信31,传统的操作系统,除了共享内存外,还有哪些跨进程通信的方法,使用场景相比于共享内存有哪些不同?32,view的绘制流程33,安卓绘制中的硬件加速的概念34,flutter是如何实现跨平台的35,flutter如何进行渲染,落实到平台上36,安卓消息机制37,flutter跟原生之间的消息通信38,服务器主动推送消息到客户端有了解吗?39,安卓本地存储,sqlite和sp的特点40,sp可以跨进程吗?如何操作?41,sp存储信息的两种方式,有什么区别?42,高德sdk,手机在室内时无法做卫星定位,如何发挥作用,误差是多少?43,网络请求框架现在普遍使用的是什么?dio是一种什么样的网络请求框架?
点赞 评论 收藏
分享
04-30 16:52
已编辑
南京航空航天大学 C++
没有开摄像头,但是解释了一下原因。总体来说体验比二面好多了。1、自我介绍2、实习时间确认3、C++和C语言的区别「面向过程和面向对象、库的优势、三大特性、内存管理」4、C++和C语言调用一个函数的区别?(回答了函数重载,类成员来调用,this指针)A* a = nullptr;a.dosomethis();a对象的作用?——a的地址初始化了this指针,this指针默认在成员函数的第一个形参5.多态如何实现?「虚表」(画图讲解更清晰)派生类重写覆盖的表述,派生类覆盖基类的虚表,(派生类和基类的虚表相同(具体是什么关系)?)6.多态有什么好处,具体应用?7.用C语言实现多态?回答到了网络套接字的(sockaddr)(sockaddr_in)的多态设计。用一个标记来区分:AF_INET/AF_UNIX做区分send函数不仅需要区分,还需要一个可调用对象(函数指针)来执行不同的动作。8、类内部的public,private,protected内存中成员在内存中需要有标记(public,private,protected的标记)吗?实际上是在编译阶段的语法分析的作用。访问private,编译就无法通过。9、项目TCmalloc主要想要解决什么问题?10、malloc的底层?如何与系统调用动态交互?11、malloc的线程安全与可重入?12、malloc内部加锁的粒度?malloc内部在读写的时候需要加锁吗?13、虚拟地址如何映射到物理地址?14、能否接受Go语言?15、反问   
点赞 评论 收藏
分享
评论
1
2
分享

创作者周榜

更多
牛客网
牛客企业服务