深拷贝和浅拷贝的区别

本文介绍了Java中的引用拷贝与对象拷贝,区分了浅拷贝和深拷贝的区别,重点讲解了浅拷贝的常见方式如直接赋值和BeaUtil.copyProperties,以及深拷贝的实现,包括通过clone方法和序列化。特别提到String类型的特殊情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

引用拷贝和对象拷贝

(1)引用拷贝:将对象的引用赋值给引用类型的变量

Object obj = new Object();
Object obj1 = obj;

(2)对象拷贝
创建对象的一个副本,其中副本和源对象的引用是两个不同的地址,而不是把一个引用赋值给一个新的引用类型的变量

class Demo{
    public static void main(String[] args) throws CloneNotSupportedException {
        User user = new User();
        User user1 = (User)user.clone();
        System.out.println(user);
        System.out.println(user1);
    }
}

class User implements Cloneable {
    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

浅拷贝和深拷贝都是对象拷贝

  • 浅拷贝:源对象和拷贝对象的存放地址不同,但被复制的源对象的引用类型属性存放的地址仍然和源对象的引用类型属性相同,修改引用类型属性的属性会影响相互影响。
  • 深拷贝:源对象和拷贝对象的存放地址不同,源对象和拷贝对象各自的引用类型存放的地址也和源对象的引用类型属性不同,修改引用类型属性的属性不会相互影响拷贝对象和源对象。

常见的浅拷贝方式

(1)一个引用类型变量直接赋值给另一个变量
(2)BeaUtil.copyProperties()

常见的深拷贝方式

(1)通过构造器或new的方式
(2)重写继承至Object方法的clone方法,并实现Cloneable接口
如果在没有实现Cloneable 接口的实例上调用 Object 的 clone 方法,则会导致抛出 CloneNotSupportedException 异常,实现Cloneable 接口的目的是:为了告知JVM,此对象允许拷贝

public class Main {
    public static void main(String[] args) throws CloneNotSupportedException {
        User user = new User(1, "测试");
        User user1 = user.clone();
        System.out.println(user1);
    }
}

class User implements Cloneable{
    private Integer id;
    private String name;
	//必须重写此方法
    @Override
    protected User clone() throws CloneNotSupportedException {
        return (User)super.clone();
    }

    public User(Integer id, String name) {
        this.id = id;
        this.name = name;
    }
}

注意: 当通过Cloneable接口实现深拷贝时,拷贝的源对象的属性如果是引用对象(String类型除外),则该属性也需要实现Cloneable接口,并覆写clone方法,否则该属性是进行的浅拷贝。

深拷贝特殊情况(String类型)

String类型属性:Clonable拷贝后两个对象的相同属性指向了通过字符串,但字符串是不可以修改的,存放在常量池中,当其中1个对象的该属性指向另个字符串时,另外一个对象的属性的引用仍指向原字符串。

public class ProtoTypeDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person();
        person.setId(1L);
        person.setName("test");

        User user = new User();
        user.setUsername("测试");
        person.setUser(user);

        Person person1 = person.clone();
        User user1 = user.clone();
        user1.setUsername("cs");
        person1.setUser(user1);
        System.out.println(person1);//Person{id=1, name='test', user=User{username='cs'}}
        System.out.println(person);//Person{id=1, name='test', user=User{username='测试'}}
    }
}
public class Person implements Cloneable {
    private Long id;
    private String name;
    private User user;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    @Override
    public Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }
    public User getUser() {
        return user;
    }
    public void setUser(User user) {
        this.user = user;
    }
    @Override
    public String toString() {
        return "Person{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", user=" + user +
                '}';
    }
}
public class User implements Cloneable{
    private String username;
    public String getUsername() {
        return username;
    }
    public void setUsername(String username) {
        this.username = username;
    }
    @Override
    public User clone() throws CloneNotSupportedException {
        return (User) super.clone();
    }
    @Override
    public String toString() {
        return "User{" +
                "username='" + username + '\'' +
                '}';
    }
}

(3)序列化的方式:FastJson、Gson等都可以

class Main{
    public static void main(String[] args) {
        User user = new User(1, "测试");
        //序列化
        String s = JSONObject.toJSONString(user);
        //反序列化
        System.out.println(s);
        User user1 = JSON.parseObject(s, User.class);
        System.out.println(user1);
    }

}
public class User {
    private Integer id;
    private String name;


    public User(Integer id, String name) {
        this.id = id;
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}
### 深拷贝浅拷贝的概念 在 Python 中,深拷贝(Deep Copy)浅拷贝(Shallow Copy)用于创建对象的副本。两者的主要区别在于如何处理嵌套对象以及这些对象之间的关系。 #### 浅拷贝(Shallow Copy) 浅拷贝仅复制顶层容器的内容,而不会递归地复制内部对象。这意味着如果原对象包含其他可变对象作为其成员,则新对象中的对应成员仍然是原始成员的引用[^1]。因此,在修改嵌套对象时,会影响两个对象的状态。 以下是浅拷贝的一个例子: ```python import copy a = [1, 2, [3, 4]] b = copy.copy(a) # 创建浅拷贝 print(id(a)) # 原始对象ID print(id(b)) # 复制后的对象ID print(id(a[2])) # 嵌套子对象ID print(id(b[2])) # 子对象仍共享同一引用 ``` 可以看到 `a` `b` 是不同的对象,但由于浅拷贝的原因,`a[2]` `b[2]` 实际上指向同一个列表实例[^3]。 --- #### 深拷贝(Deep Copy) 深拷贝不仅复制顶层容器本身,还会递归地复制所有嵌套的对象。这使得源对象与其副本完全独立,即使其中一个对象被修改也不会影响另一个对象[^2]。 下面是一个深拷贝的例子: ```python import copy a = [1, 2, [3, 4]] c = copy.deepcopy(a) # 创建深拷贝 print(id(a)) # 原始对象ID print(id(c)) # 完全新的对象ID print(id(a[2])) # 嵌套子对象ID print(id(c[2])) # 新的嵌套子对象ID ``` 通过观察可以发现,无论是外层还是内层对象,`a` `c` 都不再有任何关联。任何一方的变化都不会波及另一方。 --- ### 性能对比 由于深拷贝涉及更多的资源消耗(因为它需要逐级复制整个结构),所以它的执行效率通常低于浅拷贝。然而,这种额外开销换来的是更高的安全性,特别是在复杂数据结构的操作场景下[^2]。 --- ### 使用建议 - 如果目标只是简单的一维数组或者不可变类型的数据,可以直接赋值或采用浅拷贝即可满足需求。 - 对于复杂的、多层次的数据结构,尤其是那些可能包含大量可变元素的情况,推荐使用深拷贝来避免意外状态同步问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值