Java的深拷贝和浅拷贝

深拷贝浅拷贝是 Java 中对象复制的两种方式,区别在于对引用类型的处理方式不同:

1. 浅拷贝(Shallow Copy)

  • 概念:只复制对象本身及其基本类型字段,对引用类型字段只复制引用地址,不会复制引用指向的实际对象。
  • 特点
    • 原对象和拷贝对象共享引用类型字段的内存地址。
    • 修改引用类型字段会同时影响原对象和拷贝对象。
  • 实现方式
    • 调用 Object 类的 clone() 方法(默认实现的 clone() 是浅拷贝)。
    • 手动赋值非基本类型的字段。
class Person implements Cloneable {
    String name;
    int age;
    Address address;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone(); // 浅拷贝
    }
}

class Address {
    String city;
}
Person p1 = new Person();
p1.name = "Alice";
p1.address = new Address();
p1.address.city = "New York";

Person p2 = (Person) p1.clone(); // 浅拷贝
p2.address.city = "London";

System.out.println(p1.address.city); // 输出 "London",共享地址。

2. 深拷贝(Deep Copy)

  • 概念:复制对象本身及其所有引用类型字段指向的实际对象,创建独立的副本。
  • 特点
    • 原对象与拷贝对象互不影响,彼此独立。
    • 修改任何一个对象的引用字段不会影响另一个对象。
  • 实现方式
    1. 手动实现:递归复制每个引用字段。
    2. 序列化和反序列化:将对象序列化后再反序列化得到新对象。
    3. 第三方工具:如 Apache Commons Lang 提供的 SerializationUtils
class Person implements Cloneable {
    String name;
    int age;
    Address address;

    @Override
    protected Object clone() throws CloneNotSupportedException {
        Person cloned = (Person) super.clone();
        cloned.address = new Address(); // 深拷贝地址
        cloned.address.city = this.address.city;
        return cloned;
    }
}
Person p1 = new Person();
p1.name = "Alice";
p1.address = new Address();
p1.address.city = "New York";

Person p2 = (Person) p1.clone(); // 深拷贝
p2.address.city = "London";

System.out.println(p1.address.city); // 输出 "New York",独立副本。

3. 总结

  • 浅拷贝:快速但存在引用共享风险,适合轻量级对象。
  • 深拷贝:更加安全但性能开销较大,适合复杂对象或引用嵌套对象。

在 JVM 中,浅拷贝深拷贝的内存本质区别主要体现在堆内存中对象的分配和引用字段的指向上。以下从内存模型的角度来讲解这两者的差异:


1. JVM 内存模型概述

  • 堆(Heap):存储对象实例和数组,所有对象都在堆内存中分配。
  • 栈(Stack):存储方法的局部变量,包括原始数据类型变量和对象引用。
  • 方法区(Method Area):存储类的元信息、静态变量等。
  • 直接内存:与 NIO 等相关的直接分配内存。
  • 程序计数器和本地方法栈:线程相关的执行信息。

拷贝操作主要涉及 栈中的引用堆中的对象实例


2. 浅拷贝的内存模型

浅拷贝过程

  • 对象的基本类型字段会复制其值。
  • 引用类型字段只复制引用地址,而不是实际对象。

内存情况

  1. 堆内存:只有原始对象本身被复制,引用字段仍指向相同的堆内存地址。
  2. 栈内存:新对象在栈中存储了新的引用,但这些引用指向的堆内存仍然是原对象的引用。

内存图示

假设有一个类:

class Person {
    String name;        // 引用类型
    int age;            // 基本类型
    Address address;    // 引用类型
}

class Address {
    String city;
}

浅拷贝代码:

Person p1 = new Person();
p1.name = "Alice";
p1.age = 25;
p1.address = new Address();
p1.address.city = "New York";

Person p2 = (Person) p1.clone(); // 浅拷贝

内存分布:

  • p1、p2
    • 栈中存储两份 Person 的引用。
  • 堆中
    • p1p2 的基本类型 age 独立存储。
    • p1.addressp2.address 指向同一个堆内存的 Address 对象。

结果:修改 p2.address.city 会影响 p1.address.city


3. 深拷贝的内存模型

深拷贝过程

  • 对象的基本类型字段会复制其值。
  • 对于引用类型字段,会递归复制其引用指向的对象,从而生成完全独立的副本。

内存情况

  1. 堆内存:每个引用字段都会创建新的对象实例,所有引用都指向新分配的堆内存。
  2. 栈内存:新对象的引用独立存储,完全不共享堆内存地址。

内存图示

深拷贝代码:

Person p2 = (Person) p1.clone(); // 假设 clone 方法实现了深拷贝

内存分布:

  • p1、p2
    • 栈中存储两份 Person 的引用。
  • 堆中
    • p1p2 的基本类型字段独立存储。
    • p2.address 指向新分配的 Address 对象,与 p1.address 完全独立。

结果:修改 p2.address.city 不会影响 p1.address.city


4. 浅拷贝与深拷贝内存分布对比

特性浅拷贝深拷贝
基本类型字段复制值复制值
引用类型字段复制引用地址,共享同一堆内存对象递归复制引用指向的对象,堆内存中生成独立副本
栈内存分配两个独立的对象引用两个独立的对象引用
堆内存分配原对象与拷贝对象共享部分内存原对象与拷贝对象的内存完全独立
修改引用字段对原对象影响会影响不会影响

5. 适用场景与注意点

  1. 浅拷贝

    • 适用于性能要求高,且对象内部没有复杂引用类型的场景。
    • 避免在共享引用字段修改时引发数据问题。
  2. 深拷贝

    • 适用于对象复杂、存在嵌套引用类型,并需要完全独立的副本时。
    • 深拷贝实现成本高,可能影响性能。

6. 内存讲解总结

  • 浅拷贝:栈中的引用地址复制,堆中部分内存共享。
  • 深拷贝:栈中的引用地址和堆中所有内容都独立分配,从而保证数据完全隔离。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

堕落年代

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值