java干货 浅拷贝和深拷贝

一、浅拷贝

1.1 特点

  • 基本数据类型字段的拷贝:值被复制,新对象和原对象的字段在内存中是不同
  • 引用类型字段的拷贝:对于引用类型,它们的引用被复制,他们指向同一个内存空间,是同一份数据。
    注意:当引用类型字段发生改变时,不可变引用类型和可变引用类型是不同的,前者是线程安全的,后者是线程不安全的:
    • 如:因为 String 是不可变的,当新对象的String 类型字段 发生变化时,但是原对象的同一String 字段不变。这是因为新对象的String 字段只是指向另一个内存堆空间。如 String a = “123”; String b = a; a = “456”; a 指向的新的内存空间,并不是将“123”所在空间的值改为“456”,这是String 底层使用不可变数组导致的。a 的 改变不会引起b的改变,因此String 是线程安全的

1.2 代码实现

  • 通过构造函数 传递对象 实现浅拷贝 不可变引用类型
class Person {
    private String name;
    private int age;

    // Constructor
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    // Copy constructor for shallow copy
    public Person(Person other) {
        this.name = other.name; // Assigning reference of other.name to this.name
        this.age = other.age;
    }

    // Getters and Setters
    public String getName() {
        return name;
    }

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

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public static void main(String[] args) {
        Person original = new Person("John", 30);
        Person copy = new Person(original);
        
        System.out.println(original.name == copy.name);
        
        // 改变 拷贝对象的 引用类型字段的值
        copy.setName("Doe");

        System.out.println(original.name == copy.name);
    }
}
true
false

分析:对象original 和 copy 的name 字段通过引用赋值,实现浅拷贝,两个对象的name 引用指向同一内存空间。当修改copy 的name 的值后,两者本应该还是指向同一个内存空间,但是实际却不是。这是因为name 是String 类型,String 不可变,给copy 的name 赋 新的值,实际上是开辟了新的内存空间,copy 的name 指向这一个新空间,而 original 还是指向原来那个空间,那个空间的值没变,也不可变。

修改基本类型的值

public static void main(String[] args) {
        Person original = new Person("John", 30);
        Person copy = new Person(original);

        System.out.println("original: " + original.toString());
        System.out.println("copy: " + copy.toString());
        // 改变 拷贝对象的 引用类型字段的值
        copy.setAge(5);
        System.out.println("-------------改变copy 的基本类型 值后:-----------------");
        System.out.println("original: " + original.toString());
        System.out.println("copy: " + copy.toString());
    }

运行结果:说明基本类型浅拷贝,不是同一份

original: Person{name='John', age=30}
copy: Person{name='John', age=30}
-------------改变copy 的基本类型 值后:-----------------
original: Person{name='John', age=30}
copy: Person{name='John', age=5}
  • 通过构造函数 传递对象 实现浅拷贝 可变引用类型
class Person {
    private List<String> hobbies;
    private int age;

    // Constructor
    public Person(List<String> hobbies, int age) {
        this.hobbies = hobbies;
        this.age = age;
    }

    // Copy constructor for shallow copy
    public Person(Person other) {
        this.hobbies = other.hobbies; // Reference copy for List (mutable)
        this.age = other.age;         // Value copy for int (primitive)
    }

    // Getters and Setters
    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{hobbies=" + hobbies + ", age=" + age + "}";
    }

    public static void main(String[] args) {
        List<String> hobbies = new ArrayList<>();
        hobbies.add("Reading");
        hobbies.add("Swimming");

        Person original = new Person(hobbies, 30);
        Person copy = new Person(original);

        System.out.println("Original: " + original);
        System.out.println("Copy: " + copy);

        // Modify the hobbies list of the copy
        copy.getHobbies().add("Cycling");

        System.out.println("==============copy.getHobbies().add(\"Cycling\")之后: ==============");
        System.out.println("Original: " + original);
        System.out.println("Copy: " + copy);
    }
}
Original: Person{hobbies=[Reading, Swimming], age=30}
Copy: Person{hobbies=[Reading, Swimming], age=30}
==============copy.getHobbies().add("Cycling")之后: ==============
Original: Person{hobbies=[Reading, Swimming, Cycling], age=30}
Copy: Person{hobbies=[Reading, Swimming, Cycling], age=30}

分析:两个 对象的 List 类引用 始终指向同一个内存空间。copy 的操作list ,list 指向的空间的值发生了改变,两个仍然指向同一个内存空间,获取到的值一样

修改基本类型字段的值: 基本类型经浅拷贝,两个对象各有一份,基本类型字段所在内存空间不相同

public static void main(String[] args) {
        List<String> hobbies = new ArrayList<>();
        hobbies.add("Reading");
        hobbies.add("Swimming");

        Person original = new Person(hobbies, 30);
        Person copy = new Person(original);

        System.out.println("Original: " + original);
        System.out.println("Copy: " + copy);

        copy.setAge(5);

        System.out.println("==============copy.getHobbies().add(\"Cycling\")之后: ==============");
        System.out.println("Original: " + original);
        System.out.println("Copy: " + copy);
    }
Original: Person{hobbies=[Reading, Swimming], age=30}
Copy: Person{hobbies=[Reading, Swimming], age=30}
==============copy.getHobbies().add("Cycling")之后: ==============
Original: Person{hobbies=[Reading, Swimming], age=30}
Copy: Person{hobbies=[Reading, Swimming], age=5}
  • 通过 实现Cloneable接口,重写clone 方法,实现浅拷贝
package com.binbin.copy;
import java.util.ArrayList;
import java.util.List;
class Person implements  Cloneable{
    private List<String> hobbies;
    private int age;

    // Constructor
    public Person(List<String> hobbies, int age) {
        this.hobbies = hobbies;
        this.age = age;
    }

    // Copy constructor for shallow copy
    public Person(Person other) {
        this.hobbies = other.hobbies; // Reference copy for List (mutable)
        this.age = other.age;         // Value copy for int (primitive)
    }

    // Getters and Setters
    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{hobbies=" + hobbies + ", age=" + age + "}";
    }

    @Override
    public Person clone() {
        try {
            Person clone = (Person) super.clone();
            // TODO: copy mutable state here, so the clone can't change the internals of the original
            return clone;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }

    public static void main(String[] args) {
        List<String> hobbies = new ArrayList<>();
        hobbies.add("Reading");
        hobbies.add("Swimming");

        Person original = new Person(hobbies, 30);
        Person copy = new Person(original);

        System.out.println("Original: " + original);
        System.out.println("Copy: " + copy);

        copy.getHobbies().add("cycle");

        System.out.println("==============copy.getHobbies().add(\"Cycling\")之后: ==============");
        System.out.println("Original: " + original);
        System.out.println("Copy: " + copy);
    }
}

二、深拷贝

2.1 特点

  • 在 Java 中是指创建一个新对象,同时递归地复制所有引用类型的字段,这样使得新对象完全独立于原对象。新对象的引用类型字段指向新的的内存空间基本类型字段也是新的内存空间
  • 代码实现
import java.util.ArrayList;
import java.util.List;

class Person  {
    private List<String> hobbies;
    private int age;

    // Constructor
    public Person(List<String> hobbies, int age) {
        this.hobbies = new ArrayList<>(hobbies); // Ensure independent copy for constructor
        this.age = age;
    }

    // Deep copy method
    public Person deepCopy() {
        List<String> copiedHobbies = new ArrayList<>(this.hobbies);
        return new Person(copiedHobbies, this.age);
    }

    // Getters and Setters
    public List<String> getHobbies() {
        return hobbies;
    }

    public void setHobbies(List<String> hobbies) {
        this.hobbies = hobbies;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{hobbies=" + hobbies + ", age=" + age + "}";
    }

    public static void main(String[] args) {
        List<String> hobbies = new ArrayList<>();
        hobbies.add("Reading");
        hobbies.add("Swimming");

        Person original = new Person(hobbies, 30);
        Person copy = original.deepCopy();

        System.out.println("Original: " + original);
        System.out.println("Copy: " + copy);

        // Modify the age of the copy
        copy.setAge(35);

        // Modify the hobbies list of the copy
        copy.getHobbies().add("Cycling");

        System.out.println("After modifications:");
        System.out.println("Original: " + original);
        System.out.println("Copy: " + copy);
    }
}
Original: Person{hobbies=[Reading, Swimming], age=30}
Copy: Person{hobbies=[Reading, Swimming], age=30}
After modifications:
Original: Person{hobbies=[Reading, Swimming], age=30}
Copy: Person{hobbies=[Reading, Swimming, Cycling], age=35}

分析:original 和 copy 的 hobbies 指向不同内存间,copy的hobbies 修改 不影响 original 的hobbies

三、总结

  • 深拷贝和浅拷贝的区别:深拷贝把给引用类型字段申请新的内存空间,并将原对象的数据复制到新的对象的空间中。浅拷贝只是引用赋值,还是指向同一内存空间
  • 浅拷贝还要考虑 可变引用类型字段 和 不可变引用类型字段
  • 浅拷贝可以实现Cloneable接口,重写clone 方法实现浅拷贝
  • 深拷贝的浅拷贝的基本类型字段拷贝时,都会创建新的空间
  • 41
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值