5分鐘掌握java對象的深拷貝和淺拷貝

2024年2月6日 19点热度 0人点赞

我們知道,在計算機內存中,每個對象都有一個地址,這個地址指向對象在內存中存儲的位置。當我們使用變量引用一個對象時,實際上是將該對象的地址賦值給變量。因此,如果我們將一個對象復制到另一個變量中,實際上是將對象的地址復制到了這個變量中。這就涉及到我們今天要說的深拷貝和淺拷貝。

一、深拷貝和淺拷貝的區別

1、淺拷貝是指將一個對象復制到另一個變量中,但是隻復制對象的地址,而不是對象本身。也就是說,原始對象和復制對象實際上是共享同一個內存地址的。

2、深拷貝是指將一個對象及其所有子對象都復制到另一個變量中,也就是說,它會創建一個全新的對象,並將原始對象中的所有屬性或元素都復制到新的對象中。因此,如果我們修改復制對象中的屬性或元素,原始對象中對應的屬性或元素不會受到影響。

二、深拷貝和淺拷貝的實現

1、淺拷貝

實現對象拷貝的類,需要實現 Cloneable 接口,並覆寫 clone() 方法。在Java中,我們常用的各種BeanUtils基本也都是淺拷貝的。

package com.demo;
public class CopyDemo {
    public static void main(String[] args) {
        Person person1 = new Person(new Car("鄂A123345"));
        Person person1Copy = person1.clone();
        System.out.println(person1.getCar() == person1Copy.getCar());
    }
}
class Car implements Cloneable{
    private final String carNo;
    public String getCarNo() {
        return carNo;
    }
    public Car(String carNo) {
        this.carNo = carNo;
    }
    @Override
    public Car clone() {
        try {
            return (Car) super.clone();
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}
class Person implements Cloneable {
    private Car car;
    public Car getCar() {
        return car;
    }
    public void setCar(Car car) {
        this.car = car;
    }
    public Person(Car car) {
        this.car = car;
    }
    @Override
    public Person clone() {
        try {
            Person person = (Person) super.clone();
            return person;
        } catch (CloneNotSupportedException e) {
            throw new AssertionError();
        }
    }
}

從輸出結構就可以看出, person1 的克隆對象和 person1 使用的仍然是同一個 Car 對象。

2、深拷貝

實現Cloneable接口,重寫clone()。在Object類中定義了一個clone方法,這個方法其實在不重寫的情況下,其實也是淺拷貝的。如果想要實現深拷貝,就需要重寫clone方法,而想要重寫clone方法,就必須實現Cloneable,否則會報
CloneNotSupportedException異常。

這裡我們簡單對 Person 類的 clone() 方法進行修改,連帶著要把 Person 對象內部的 Car 對象一起復制。

@Override
public Person clone() {
try {
    Person person = (Person) super.clone();
    person.setCar(person.getCar().clone());
    return person;
} catch (CloneNotSupportedException e) {
    throw new AssertionError();
}

之後,再次在執行一下上面的main測試代碼,就可以發現,這時候person1 的克隆對象和 person1 包含的 Car 對象已經是不同的了。

這種方式就能實現深拷貝,但是問題是如果我們在Person中有很多個對象,那麼clone方法就寫的很長,而且如果後面有修改,在Person中新增屬性,這個地方也要改。

這時候可以借助序列化來實現深拷貝。先把對象序列化成流,再從流中反序列化成對象,這樣就一定是新的對象了。

在實際開發過程中可以使用Apache Commons Lang中提供的SerializationUtils工具實現。我們先修改下上面的Person和Car類,使他們實現Serializable接口,否則是無法進行序列化的。

class Car implements Serializable
class Person implements Serializable

然後在需要拷貝的時候:

Person person1CopyNew= (Person)SerializationUtils.clone(person1);

以上詳細講解了java對象的深拷貝和淺拷貝的區別和應用,大傢不妨動手試一試,了解一下java的高級用法,有問題可以在評論區一起交流!