深拷贝 与 浅拷贝

本篇文章通过学习JavaGuide:Java基础常见面试题总结(中) | JavaGuide
之后的自我学习总结!

  • 浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。
  • 深拷贝:深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。

【一、基本概念】

● 浅拷贝 (Shallow Copy)

  • 复制对象本身,但是对于字段中的引用类型,复制的是引用地址

  • 新、旧对象共享同一个引用对象

● 深拷贝 (Deep Copy)

  • 不仅复制本身,还要把所有引用对象都复制一份

  • 新、旧对象完全独立,不共享内部状态


【二、Java clone() 机制】

● Object.clone()

  • Java 基类 Object 提供了 clone()

  • 默认执行 浅拷贝

● 启用 clone() 需要

  1. 实现 Cloneable 接口 (标记接口,无方法)

  2. 重写 clone() 方法

  3. 调用 super.clone() 进行复制

@Override
public ClassName clone() {
    try {
        return (ClassName) super.clone();
    } catch (CloneNotSupportedException e) {
        throw new AssertionError();
    }
}

【三、浅拷贝 vs 深拷贝 实际区别】

● 浅拷贝

class Person implements Cloneable {
    private Address address; // Address 是可变对象

    @Override
    public Person clone() {
        return (Person) super.clone();
    }
}

结果: 新、旧 Person 对象共用同一个 address

● 深拷贝

@Override
public Person clone() {
    Person p = (Person) super.clone();
    p.address = address.clone(); // 手动复制引用对象
    return p;
}

结果: 新、旧对象独立的 address


【四、注意点】

如果对象的字段全部是

  • 基本类型 (如 int, double)

  • 不可变对象 (如 String)

super.clone() 通常够用,不需要深复制

⚠ 如果字段是可变对象

  • 如 List, Map, 自定义对象

  • 那么必须手动调用 clone()


【五、为什么不推荐 clone()?】

问题说明
API 设计失败Cloneable 是标记接口,clone 异常处理复杂
难以维护多层引用对象需通通 clone
不谈经濟优化难,效率不高

推荐替代:

  1. 拷贝构造器 (Copy Constructor)

  2. 完整定制方法 (Builder 或完整 clone 手写)

  3. 应急场景:实现 Serializable 后对象序列化/反序列化


【六、小结】

区别浅拷贝深拷贝
引用对象复制只复制地址也复制对象本身
实现super.clone()手动 clone 字段
效果新、旧共享新、旧独立
风险引用冒泡、数据互相影响无风险,安全复制

如非必要,建议利用拷贝构造器或手动构造而非 clone()方法,避免隐怪 bug

下面看一个例子帮助理解:

public class copylearn {
    public static void main(String[] args) {
        Person person1 = new Person(new Address("广州"));
        Person person1copy = person1.clone();

        System.out.println(person1 == person1copy);
        System.out.println(person1.getAddress() == person1copy.getAddress());
        System.out.println(person1copy.getAddress().getName());
    }

    public static class Address implements Cloneable{
        private String name;

        public Address(String name) {
            this.name = name;
        }
        public String getName() {return name;}
        public void setName(String name) {this.name = name;}

        @Override
        public Address clone(){
            try{
                return(Address) super.clone();
            }catch(CloneNotSupportedException e){
                throw new AssertionError();
            }
        }

    }
    public static class Person implements Cloneable{
        private Address address;
        public Person(Address address) {
            this.address = address;
        }
        public Address getAddress() {return address;}
        public void setAddress(Address address) {this.address = address;}
        @Override
        public Person clone(){
            try{
                Person person  = (Person) super.clone();    //默认浅拷贝
                person.setAddress(address.clone());     //深拷贝Address
                return person;
            }catch(CloneNotSupportedException e){
                throw new AssertionError();
            }
        }
    }
}

结果:

false
false
广州

如果把源代码中   //深拷贝Address  这行注释掉的话,那么结果自然就是flase,true了

这里放个图片帮助理解:

上面的代码只是用来学习和测试,如果后期需要用到拷贝的话,最好还是使用构造器方法,下面给出示例:

 public static class Address {
        private String name;

        public Address(String name) {
            this.name = name;
        }

        // ✅ 拷贝构造函数
        public Address(Address other) {
            this.name = other.name; // String 是不可变的,直接赋值安全
        }

        public String getName() { return name; }
        public void setName(String name) { this.name = name; }
    }

    public static class Person {
        private Address address;

        public Person(Address address) {
            this.address = address;
        }

        // ✅ 拷贝构造函数(深拷贝 Address)
        public Person(Person other) {
            this.address = new Address(other.address); // 创建新的地址对象
        }

        public Address getAddress() { return address; }
        public void setAddress(Address address) { this.address = address; }
    }
对比项clone()拷贝构造函数
是否推荐❌ 不推荐(设计缺陷)✅ 推荐(安全、明确)
控制力差(容易误共享)强(字段你自己决定)
可读性一般
错误隐患多(必须记得实现 Cloneable + 异常处理)少(构造函数明确)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值