深拷贝和浅拷贝区别

在 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的碎碎念呀

全部评论

相关推荐

09-08 21:59
西北大学 Java
1. 这段实习里面哪个项目对你帮助比较大?2. 重构过程中怎么保证安全性的?3. 在重构过程中有遇到哪些问题吗?兼容性问题?bug?4. 之后如果要做这方面重构有什么建议或者理解?5. 上线后怎么去评估功能是否完善?6. 物联网的课程设置有什么特点?与计科有什么区别?7. 编译原理?汇编语言?8. 对嵌入式编程有什么理解?与正常的企业项目有什么区别?9. 用在服务器中的linux和用在嵌入式系统的linux有什么区别?10. 为什么嵌入式大部分用C和C++?11. 嵌入式的内存、CPU都有多大?12. 讲下面向对象的特性13.  在开发中觉得这些特性重要吗?14. 反射的机制知道吗?15. 一个java后端项目分层一般怎么做?16. 了解哪些数据库产品?17. 数据库设计表有什么原则?18. 联合索引设计有什么注意事项?19. 什么时候数据库需要分库分表?20. 项目中用过分库分表吗?21. 项目中用了哪些RPC组件?了解哪些?22. RPC组件一般网络中都会使用NIO模型,说说你了解的BIO、NIO模型23. NIO的特性基于操作系统方面有哪些了解?24. 多路IO复用对应操作系统的哪部分?25. epoll有两种模式,哪两种?有什么作用?26. 现在HTTP服务是基于NIO还是BIO?27. HTTP的网络方面的能力有什么?比如指定一个组件的能力?指定并发量?28. HTTP请求处理网络IO的能力?29. 现在我发一个HTTP请求给你,这个网络请求传输按分层来讲会怎么传输?封装和拆包的过程30. 以太包到哪就结束了?然后拆包去传输?31. 整个传输过程中每次拆包都会要CPU参与吗?32. HDFS和K8S有接触吗?消息队列有用到吗?33. 算法:找到链表倒数第n个节点
点赞 评论 收藏
分享
09-19 15:57
南开大学 Java
📍面试公司:字节🕐面试时间:9月19💻面试岗位:后端❓面试问题:只说我觉得稍微有难度的,基础的不写了一.八股1.ARP欺骗是什么(先问的我输入一个网址到回显的过程,我中途提到了arp会去找mac地址,然后就问我ARP欺骗是什么,凭感觉说了下)2.消息队列怎么保证三个层面可靠性,有序性,不丢失?3.消息队列有很多个,如何取舍选择哪个呢?4.详细说说堆这个数据结构5.进程间通信方式?各自的优缺点。二.场景题你有一台机器,很小的内存,现在你有一个很大的日志,日志里面只记录了IP地址,实现一个算法找出访问次数最大的IP(我说用hash,面试官说ip都装不下,你map里又要记录ip又要记录次数,能装下吗?后面说分治)三.项目1.为什么用http和mq通信?为什么不用RPC?2.消息队列如何保证不重复消费?存在redis里的话用什么数据结构?什么时候移除?3.这个项目有哪些表?追问订单信息这个表构建索引了吗?应该怎么对这个表构建索引?哪些字段加索引?4.讲讲微服务间如果通信?讲讲锁单/结算/退单流程?5.讲讲动态配置中心怎么实现的?限流器怎么实现的,为什么用本地缓存,能用redis吗,redis怎么实现限流器?四.手撕删除倒数第n个链表节点(面试官有点皮,故意让我输入需要特判的情况,比如n大于链表长度了,比如删除了链表的头节点)🙌面试感想:还行,百分之95都答上了,继续加油!!!
查看12道真题和解析
点赞 评论 收藏
分享
评论
2
2
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务