最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

關于Java中Clonable接口和深拷貝詳解

 更新時間:2026年01月05日 11:03:25   作者:Porunarufu  
Cloneable是一個接口,實現這個接口后,可以在類中重寫Object中的clone方法,然后通過類調用clone方法進行克隆,這篇文章主要介紹了關于Java中Clonable接口和深拷貝的相關資料,需要的朋友可以參考下

前言

在 Java 中,Cloneable 接口是實現對象拷貝的核心機制之一,但它默認僅支持 淺拷貝;而 深拷貝 是基于淺拷貝的進階需求,用于解決引用類型成員共享的問題。

一、先搞懂:Cloneable接口是什么?

1. 核心定位:標記接口(Marker Interface)

Cloneable 是 Java 中的 標記接口(無任何抽象方法),僅用于告訴 JVM:“這個類的對象允許被克?。?/span>clone())”。

  • 定義(源碼極簡):
    public interface Cloneable {} // 無任何方法!
  • 關鍵依賴:克隆的核心實現并非來自 Cloneable 接口,而是來自 java.lang.Object 類的 clone() 方法
    // Object 類的 clone() 方法(protected 修飾,需重寫才能公開調用)
    protected native Object clone() throws CloneNotSupportedException;

2. 淺拷貝(Shallow Copy):Cloneable的默認行為

(1)淺拷貝的定義

當對象被淺拷貝時,會創(chuàng)建一個 新的對象實例,但對象中的 引用類型成員變量 不會被復制(新對象和原對象共享同一個引用類型成員)。

  • 簡單說:“拷貝對象本身,不拷貝對象里的‘引用子對象’”。

(2)實現淺拷貝的3 個步驟

  1. 類實現 Cloneable 接口(標記允許克?。?/span>
  2. 重寫 Object 類的 clone() 方法(提升訪問權限為 public,處理異常);
  3. 調用 super.clone() 完成淺拷貝(JVM 原生實現對象拷貝)。

(3)代碼示例:淺拷貝實戰(zhàn)

// 引用類型:地址(被 User 類引用)
class Address {
    private String city;

    public Address(String city) {
        this.city = city;
    }

    // getter/setter
    public String getCity() { return city; }
    public void setCity(String city) { this.city = city; }
}

// 實現 Cloneable 接口,支持淺拷貝
class User implements Cloneable {
    private String name; // 基本類型包裝類(不可變,淺拷貝無問題)
    private int age;     // 基本類型(淺拷貝直接復制值)
    private Address addr;// 引用類型(淺拷貝僅復制引用,共享對象)

    // 構造方法
    public User(String name, int age, Address addr) {
        this.name = name;
        this.age = age;
        this.addr = addr;
    }

    // 重寫 clone() 方法:實現淺拷貝
    @Override
    public User clone() throws CloneNotSupportedException {
        // 調用 Object 的 clone(),JVM 會創(chuàng)建新對象并復制成員值
        return (User) super.clone();
    }

    // getter/setter
    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 Address getAddr() { return addr; }
}

// 測試淺拷貝
public class ShallowCopyTest {
    public static void main(String[] args) throws CloneNotSupportedException {
        // 1. 創(chuàng)建原對象
        Address addr = new Address("北京");
        User user1 = new User("張三", 20, addr);

        // 2. 淺拷貝得到新對象
        User user2 = user1.clone();

        // 3. 驗證:基本類型/不可變類型的拷貝(獨立)
        System.out.println(user1.getName() == user2.getName()); // true(String 不可變,共享無問題)
        user2.setName("李四");
        user2.setAge(22);
        System.out.println(user1.getName()); // 張三(原對象不受影響)
        System.out.println(user1.getAge());  // 20(原對象不受影響)

        // 4. 驗證:引用類型的拷貝(共享)
        System.out.println(user1.getAddr() == user2.getAddr()); // true(同一個 Address 對象)
        user2.getAddr().setCity("上海"); // 修改 user2 的 Address
        System.out.println(user1.getAddr().getCity()); // 上海(原對象的 Address 也被修改?。?
    }
}

(4)淺拷貝的問題

當對象包含 可變的引用類型成員(如 Address)時,淺拷貝會導致原對象和拷貝對象共享該成員,修改其中一個會影響另一個,破壞對象的 “獨立性”—— 這就是需要深拷貝的場景。

二、深拷貝(Deep Copy):解決引用類型共享問題

1. 深拷貝的定義

深拷貝會創(chuàng)建一個 完全獨立的新對象:不僅拷貝對象本身,還會遞歸拷貝對象中所有 可變的引用類型成員變量,最終新對象和原對象的引用類型成員指向不同的內存地址,互不影響。

  • 簡單說:“拷貝對象本身 + 拷貝對象里所有的‘引用子對象”

2. 深拷貝的 3 種實現方式(實戰(zhàn)常用)

方式 1:重寫clone()方法,手動遞歸拷貝引用類型

核心思路:在淺拷貝的基礎上,對每個引用類型成員也調用其 clone() 方法(需讓引用類型也實現 Cloneable)。

// 步驟1:讓引用類型 Address 也實現 Cloneable,支持拷貝
class Address implements Cloneable {
    private String city;

    public Address(String city) { this.city = city; }

    // 重寫 clone() 方法
    @Override
    public Address clone() throws CloneNotSupportedException {
        return (Address) super.clone();
    }

    // getter/setter 略
}

// 步驟2:User 類的 clone() 中,手動拷貝 Address
class User implements Cloneable {
    private String name;
    private int age;
    private Address addr;

    // 構造方法略

    @Override
    public User clone() throws CloneNotSupportedException {
        // 第一步:淺拷貝 User 對象本身
        User newUser = (User) super.clone();
        // 第二步:手動拷貝引用類型成員(深拷貝核心)
        newUser.addr = this.addr.clone(); // 遞歸拷貝 Address
        return newUser;
    }

    // getter/setter 略
}

// 測試深拷貝
public class DeepCopyTest1 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Address addr = new Address("北京");
        User user1 = new User("張三", 20, addr);
        User user2 = user1.clone();

        // 驗證:引用類型成員不再共享
        System.out.println(user1.getAddr() == user2.getAddr()); // false(不同 Address 對象)
        user2.getAddr().setCity("上海");
        System.out.println(user1.getAddr().getCity()); // 北京(原對象不受影響)
        System.out.println(user2.getAddr().getCity()); // 上海(新對象獨立修改)
    }
}

方式 2:通過序列化(Serializable)實現深拷貝

核心思路:利用 Java 序列化將對象寫入流(序列化),再從流中讀取(反序列化),生成的新對象是完全獨立的(無需手動遞歸拷貝)。

  • 優(yōu)點:無需讓每個引用類型都實現 Cloneable,適合復雜對象(多層引用嵌套);
  • 缺點:需要所有成員變量都實現 Serializable 接口,效率略低于手動克隆。
import java.io.*;

// 步驟1:所有相關類實現 Serializable 接口(標記可序列化)
class Address implements Serializable {
    private String city;
    public Address(String city) { this.city = city; }
    // getter/setter 略
}

class User implements Serializable {
    private String name;
    private int age;
    private Address addr;

    public User(String name, int age, Address addr) {
        this.name = name;
        this.age = age;
        this.addr = addr;
    }

    // 步驟2:實現深拷貝工具方法(序列化+反序列化)
    public User deepCopy() throws IOException, ClassNotFoundException {
        // 1. 序列化:將對象寫入字節(jié)流
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this); // 寫入當前 User 對象

        // 2. 反序列化:從字節(jié)流讀取新對象
        ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bis);
        return (User) ois.readObject(); // 生成獨立的新對象
    }

    // getter/setter 略
}

// 測試序列化深拷貝
public class DeepCopyTest2 {
    public static void main(String[] args) throws Exception {
        Address addr = new Address("北京");
        User user1 = new User("張三", 20, addr);
        User user2 = user1.deepCopy();

        // 驗證:完全獨立
        System.out.println(user1.getAddr() == user2.getAddr()); // false
        user2.getAddr().setCity("上海");
        System.out.println(user1.getAddr().getCity()); // 北京
        System.out.println(user2.getAddr().getCity()); // 上海
    }
}

方式 3:使用第三方工具(簡化開發(fā))

實際開發(fā)中,可借助成熟工具類避免重復編碼,常用的有:

  • Apache Commons LangSerializationUtils.clone()(基于序列化,無需手動寫流操作);
  • Gson/Jackson:將對象轉為 JSON 字符串,再轉回對象(間接實現深拷貝,支持復雜對象)。

示例(Apache Commons Lang):

  1. 引入依賴(Maven):
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.14.0</version>
    </dependency>
  2. 代碼實現:
    import org.apache.commons.lang3.SerializationUtils;
    
    // 所有類仍需實現 Serializable
    class User implements Serializable { /* 成員變量、構造方法略 */ }
    
    public class DeepCopyTest3 {
        public static void main(String[] args) {
            Address addr = new Address("北京");
            User user1 = new User("張三", 20, addr);
            // 一行代碼實現深拷貝
            User user2 = SerializationUtils.clone(user1);
    
            System.out.println(user1.getAddr() == user2.getAddr()); // false
        }
    }

3. 深拷貝的注意事項

  • 循環(huán)引用處理:如果對象存在循環(huán)引用(如 A 引用 BB 引用 A),手動克隆會棧溢出,序列化方式(或工具類)可自動處理;
  • 不可變類型無需深拷貝:如 String、Integer 等不可變類型,淺拷貝時共享引用無問題(無法修改原值);
  • ** transient 關鍵字 **:序列化方式中,被 transient 修飾的成員變量不會被拷貝(需根據需求決定是否使用)。

三、淺拷貝 vs 深拷貝 核心區(qū)別

四、常見面試題 & 實戰(zhàn)建議

1. 面試高頻問題

(1)Cloneable接口有什么用?如果不實現它,調用clone()會怎樣?

  • 作用:標記類的對象允許被克隆(無任何方法,僅為 JVM 提供標記);
  • 不實現后果:調用 super.clone() 時會拋出 CloneNotSupportedException(運行時異常)。

(2)Object類的clone()方法是淺拷貝還是深拷貝?

默認是 淺拷貝僅復制對象的成員值(基本類型復制值,引用類型復制引用地址)。

(3)深拷貝的實現方式有哪些?各自的優(yōu)缺點?

  • 手動克隆:優(yōu)點效率高,缺點需遞歸實現,復雜對象繁瑣;
  • 序列化:優(yōu)點無需手動處理嵌套,缺點需實現 Serializable,效率略低;
  • 第三方工具:優(yōu)點簡潔高效,缺點依賴外部依賴。

(4)為什么String類型在淺拷貝中不會有問題?

String 是 不可變類型(一旦創(chuàng)建無法修改,修改時會生成新 String 對象),淺拷貝時共享引用不會導致原對象被修改,因此無需深拷貝。

2. 實戰(zhàn)選擇建議

  • 簡單對象(無可變引用類型):用 Cloneable 實現淺拷貝(高效簡潔);
  • 復雜對象(多層嵌套引用):優(yōu)先用第三方工具(如 Apache Commons Lang)或序列化(減少編碼量);
  • 性能敏感場景:手動實現深拷貝(避免序列化的性能損耗);
  • 避免過度使用克隆:如果對象可通過構造方法創(chuàng)建新實例(如 new User(user1.getName(), user1.getAge(), new Address(...))),可直接用構造方法替代克?。ǜ鬃x、無接口依賴)。

總結

  • Cloneable 是 “允許克隆” 的標記接口,默認支持淺拷貝,核心依賴 Object.clone();
  • 淺拷貝適合簡單對象,深拷貝解決引用類型共享問題,需通過手動遞歸、序列化或工具類實現;
  • 實際開發(fā)中,優(yōu)先選擇第三方工具(如 SerializationUtils)實現深拷貝,兼顧簡潔性和穩(wěn)定性。

到此這篇關于關于Java中Clonable接口和深拷貝的文章就介紹到這了,更多相關Java中Clonable接口和深拷貝內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • Java中的ArrayList(擴容機制)詳解

    Java中的ArrayList(擴容機制)詳解

    ArrayList作為Java中廣泛使用的動態(tài)數組,其擴容機制是保證性能和內存使用平衡的關鍵,默認初始容量為10,擴容因子為1.5,旨在減少頻繁的內存分配和數據遷移代價,同時建議使用預估計的初始化容量以減少擴容次數
    2024-11-11
  • Java運行時動態(tài)生成類實現過程詳解

    Java運行時動態(tài)生成類實現過程詳解

    這篇文章主要介紹了Java運行時動態(tài)生成類實現過程詳解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下
    2020-07-07
  • Java之SpringCloudAlibaba Sentinel組件案例講解

    Java之SpringCloudAlibaba Sentinel組件案例講解

    這篇文章主要介紹了Java之SpringCloudAlibaba Sentinel組件案例講解,本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內容,需要的朋友可以參考下
    2021-07-07
  • 解析Arthas協(xié)助排查線上skywalking不可用問題

    解析Arthas協(xié)助排查線上skywalking不可用問題

    這篇文章主要為大家介紹了解析Arthas協(xié)助排查線上skywalking不可用的問題詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-02-02
  • JAVA用戶自定義事件監(jiān)聽實例代碼

    JAVA用戶自定義事件監(jiān)聽實例代碼

    這篇文章主要介紹了JAVA用戶自定義事件監(jiān)聽實例代碼的相關資料,需要的朋友可以參考下
    2017-04-04
  • springboot項目中引入本地依賴jar包并打包到lib文件夾中

    springboot項目中引入本地依賴jar包并打包到lib文件夾中

    這篇文章主要介紹了springboot項目中引入本地依賴jar包,如何打包到lib文件夾中,本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2023-04-04
  • @Qualifier使用及詳細源碼展示

    @Qualifier使用及詳細源碼展示

    @Qualifier是Spring解決依賴注入歧義的注解,通過限定符名稱指定具體Bean,與@Primary不同,可覆蓋主Bean,適用于多實現類和自定義限定符場景,需注意大小寫敏感及字段注入的耦合問題,配合@Autowired提升靈活性
    2025-09-09
  • 你真的了解java單例模式了嗎?

    你真的了解java單例模式了嗎?

    這篇文章主要介紹了你真的了解java單例模式了嗎?實際上單例模式有著好幾個變種,并且多線程中涉及到線程安全問題,,需要的朋友可以參考下
    2019-06-06
  • Java Collection 移除元素方法及注意事項

    Java Collection 移除元素方法及注意事項

    這篇文章主要介紹了Java Collection 移除元素方法及注意事項,通過一個簡單實例給大家講解,需要的朋友可以參考下
    2020-01-01
  • websocket實現方法日志實時查詢過程

    websocket實現方法日志實時查詢過程

    本文介紹了一種基于Redis生成唯一key的方案,前端傳遞key至服務器,服務器據此生成日志文件并通過WebSocket線程實時讀取返回內容,結合logback實現日志記錄與監(jiān)控
    2025-07-07

最新評論

安乡县| 旬邑县| 海晏县| 宁国市| 抚顺市| 商洛市| 白朗县| 五原县| 柘荣县| 泰宁县| 云梦县| 固原市| 共和县| 彰化县| 莲花县| 于都县| 兴化市| 缙云县| 开化县| 海门市| 龙游县| 浑源县| 岐山县| 汕头市| 和平县| 嘉义县| 汉川市| 福安市| 辽阳市| 同仁县| 靖江市| 莱州市| 且末县| 合川市| 东港市| 桑植县| 香格里拉县| 八宿县| 乌海市| 溧水县| 商丘市|