深浅复制详解及Cloneable接口实现

80 阅读4分钟

前言

在Java编程中,对象复制不仅涉及到数据的传递,更关乎程序的结构设计和资源管理,深入理解深拷贝与浅拷贝的概念及其在实际编程中的应用,有助于开发者编写出更加健壮、灵活且易于维护的代码。本文将详细探讨这两种复制方式的区别,以及如何利用Java的Cloneable接口来实现它们,帮助大家在面试和实际工作中更加游刃有余地应对相关问题。

一、深拷贝与浅拷贝的区别

首先先简单介绍一下深拷贝与浅拷贝的区别:

浅拷贝

  • 浅拷贝创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。
  • 如果属性是基本类型,拷贝的就是基本类型的值;如果属性是引用类型,拷贝的就是内存地址,因此如果其中一个对象改变了这个地址,就会影响到另一个对象。

深拷贝

  • 深拷贝在拷贝引用类型时会拷贝地址指向的内容,而不会拷贝地址本身。
  • 深拷贝出的对象与原对象没有任何关联,修改其一不会影响另一方的任何属性。

二、为什么需要深拷贝?

为什么需要深拷贝?其实主要有下面两点优点:

  1. 避免共享引用:深拷贝确保复制的是对象的真正副本,与原对象没有引用关系,避免了修改副本影响原对象的问题。
  2. 线程安全:在多线程环境中,深拷贝可以避免多个线程同时修改同一个对象导致的数据不一致问题。

三、使用Cloneable接口实现深浅复制

接下来就用代码案例是分析讲解深拷贝与浅拷贝。 Java提供了Cloneable接口来支持对象的复制。实现Cloneable接口的类需要重写clone()方法。默认情况下,clone()方法执行的是浅拷贝。

简单例子
public class DeepCopyTest {
    public static void main(String[] args) {
        Person originalPerson = new Person("John", 25);
        Person copiedPerson = new Person(originalPerson); // 深拷贝
        Person shallowPerson = originalPerson; // 浅拷贝

        originalPerson.setName("Mike");
        originalPerson.setAge(30);

        System.out.println("原 Person: " + originalPerson.getName() + ", " + originalPerson.getAge());
        System.out.println("深拷贝 Person: " + copiedPerson.getName() + ", " + copiedPerson.getAge());
        System.out.println("浅拷贝 Person: " + shallowPerson.getName() + ", " + shallowPerson.getAge());
    }
}

class Person {
    private String name;
    private int age;

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

    public Person(Person person) {
        this.name = person.name;
        this.age = person.age;
    }

    // getters and setters
}

在这个上面例子中,Person类的拷贝构造函数实现了深拷贝,确保了新对象与原对象的独立性,是比较简单的一种方式,运行结果如下,发现修改原始对象属性,浅拷贝对象也会改变。

image.png

复杂例子

接下就是实现Cloneable接口,重写clone()方法,如果是浅拷贝直接返回父级clone方法即可,如果是深拷贝这需要重新创建对象,如下:

    protected Object clone() throws CloneNotSupportedException
    {
        //浅拷贝
//        return super.clone();
        // 深拷贝,创建对象
        return new Emp(empName,age,boss.getBossName(),boss.getTitle());

    }

最终完整案例代码如下:

public class ShallowDeepCopyDemo {
    public static void main(String[] args) throws CloneNotSupportedException {
        Emp emp = new Emp("z3", 15, "雷军", "CEO");
        Emp emp2 = (Emp) emp.clone();

        emp2.getBoss().setTitle("CTO");

        System.out.println("原始对象:" + emp.getBoss().getTitle());
        System.out.println("拷贝对象:" + emp2.getBoss().getTitle());
    }
}

class Boss implements Cloneable {
    private String bossName;
    private String title;

    // 构造函数、getters and setters

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return super.clone();
    }
}

class Emp implements Cloneable {
    private String empName;
    private Integer age;
    private Boss boss;

    // 构造函数、getters and setters

    @Override
    protected Object clone() throws CloneNotSupportedException {
        return new Emp(empName, age, boss.getBossName(), boss.getTitle()); // 深拷贝
    }
}

在这个复杂例子中,Emp类通过重写clone()方法实现了深拷贝,确保了即使修改了拷贝对象的Boss属性,也不会影响原对象的Boss属性,运行二级果如下:

image.png

四、总结

  • 深拷贝浅拷贝的主要区别在于对引用类型属性的处理方式。
  • 深拷贝可以避免共享引用带来的问题,提高程序的安全性和稳定性,这也是使用深拷贝的优点。
  • 使用Cloneable接口可以方便地实现对象的复制,但需要注意正确实现深拷贝逻辑。

Java的Cloneable接口提供了对象复制的机制,但实现深拷贝时需特别小心,确保所有引用类型属性都被正确复制,理解并正确应用这两种复制方式是编写高质量Java程序的关键,欢迎大家在评论区指点。