Java面试必备:深拷贝、浅拷贝、引用拷贝的区别

Java中的拷贝可以分为深拷贝(Deep Copy)、浅拷贝(Shallow Copy)和引用拷贝(Reference Copy)。它们之间的区别如下:

浅拷贝:

只复制对象本身,而不复制对象包含的子对象。新旧对象之间共享子对象的引用,即新对象和原始对象中的子对象指向同一个内存地址。

浅拷贝:使用clone()方法或者Object类的copy()方法。

深拷贝:

不仅复制对象本身,还要复制对象包含的所有子对象。新对象和原始对象所包含的子对象是相互独立的。

深拷贝:可以通过序列化和反序列化、递归遍历等方式来实现。

引用拷贝:

只复制对象的引用,而不复制对象本身。新旧对象之间共享同一个对象实例,即它们的引用指向同一个内存地址。

引用拷贝:直接将对象的引用赋值给另一个变量即可。

例如,以下代码演示了引用拷贝和浅拷贝的区别:

class Person {
    public String name;
    public int age;
}


public class CopyDemo {
    public static void main(String[] args) {
        Person p1 = new Person();
        p1.name = "Alice";
        p1.age = 20;
        
        Person p2 = p1; // 引用拷贝
        
        System.out.println(p1 == p2);         // true, 引用相等
        System.out.println(p1.name == p2.name); // true, 字符串常量池中的引用相同
        System.out.println(p1.age == p2.age);   // true, 基本数据类型的值相同
        
        Person p3 = (Person)p1.clone(); // 浅拷贝
        
        System.out.println(p1 == p3);         // false, 引用不相等
        System.out.println(p1.name == p3.name); // true, 字符串常量池中的引用相同
        System.out.println(p1.age == p3.age);   // true, 基本数据类型的值相同
    }
}

在上面的代码中,p1和p2是两个对象的引用,它们指向同一个对象实例。在使用引用拷贝时,p2和p1共享同一个对象实例,所以它们的属性值相等,并且两个引用也是相等的。

而当使用浅拷贝时,p3是通过调用p1的clone()方法来复制p1的对象的。由于这种方式只是复制了对象本身,而没有复制对象包含的子对象,所以p1和p3引用不同的对象实例。但由于name属性的值是字符串常量,字符串常量池中只有一个实例,所以p1和p3的name属性指向同一个对象。但age是一个基本数据类型,不是一个对象,所以p1和p3的age属性值相等。

例如,以下代码演示了深拷贝和浅拷贝的区别:

public class Address implements Cloneable {
    private String name;  

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

public class Person implements Cloneable {
    public String name;
    public int age;
    private Address address;

    // 省略构造函数、Getter&Setter方法     
    @Override
    public Person clone() {
        try {
            Person person = (Person) super.clone();
            return person;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}
//测试 :

Person person1=new Person(new Address("武汉"));
Person person1Copy=person1.clone(); // true
System.out.println(person1.getAddress()==person1Copy.getAddress());

从输出结构就可以看出, person1 的克隆对象和 person1 使用的仍然是同一个 Address 对象。

深拷贝

这里我们简单对 Person 类的 clone() 方法进行修改,连带着要把 Person 对象内部的 Address 对象一起复制。

@Override
    public Person clone(){
        try{
            Person person=(Person)super.clone();
            person.setAddress(person.getAddress().clone());
            return person;
        }catch(CloneNotSupportedException e){
            throw new AssertionError();
        }
    }
测试 :

Person person1 = new Person(new Address("武汉")); 
Person person1Copy = person1.clone(); // false 
System.out.println(person1.getAddress() == person1Copy.getAddress());

从输出结构就可以看出,虽然 person1 的克隆对象和 person1 包含的 Address 对象已经是不同的了。

猜你喜欢

转载自blog.csdn.net/citywu123/article/details/130017495