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

關(guān)于Java中Clonable接口和深拷貝詳解

 更新時(shí)間:2026年01月05日 11:03:25   作者:Porunarufu  
Cloneable是一個(gè)接口,實(shí)現(xiàn)這個(gè)接口后,可以在類(lèi)中重寫(xiě)Object中的clone方法,然后通過(guò)類(lèi)調(diào)用clone方法進(jìn)行克隆,這篇文章主要介紹了關(guān)于Java中Clonable接口和深拷貝的相關(guān)資料,需要的朋友可以參考下

前言

在 Java 中,Cloneable 接口是實(shí)現(xiàn)對(duì)象拷貝的核心機(jī)制之一,但它默認(rèn)僅支持 淺拷貝;而 深拷貝 是基于淺拷貝的進(jìn)階需求,用于解決引用類(lèi)型成員共享的問(wèn)題

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

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

Cloneable 是 Java 中的 標(biāo)記接口(無(wú)任何抽象方法),僅用于告訴 JVM:“這個(gè)類(lèi)的對(duì)象允許被克隆(clone())”。

  • 定義(源碼極簡(jiǎn)):
    public interface Cloneable {} // 無(wú)任何方法!
  • 關(guān)鍵依賴:克隆的核心實(shí)現(xiàn)并非來(lái)自 Cloneable 接口,而是來(lái)自 java.lang.Object 類(lèi)的 clone() 方法
    // Object 類(lèi)的 clone() 方法(protected 修飾,需重寫(xiě)才能公開(kāi)調(diào)用)
    protected native Object clone() throws CloneNotSupportedException;

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

(1)淺拷貝的定義

當(dāng)對(duì)象被淺拷貝時(shí),會(huì)創(chuàng)建一個(gè) 新的對(duì)象實(shí)例,但對(duì)象中的 引用類(lèi)型成員變量 不會(huì)被復(fù)制(新對(duì)象和原對(duì)象共享同一個(gè)引用類(lèi)型成員)。

  • 簡(jiǎn)單說(shuō):“拷貝對(duì)象本身,不拷貝對(duì)象里的‘引用子對(duì)象’”。

(2)實(shí)現(xiàn)淺拷貝的3 個(gè)步驟

  1. 類(lèi)實(shí)現(xiàn) Cloneable 接口(標(biāo)記允許克?。?;
  2. 重寫(xiě) Object 類(lèi)的 clone() 方法(提升訪問(wèn)權(quán)限為 public,處理異常);
  3. 調(diào)用 super.clone() 完成淺拷貝(JVM 原生實(shí)現(xiàn)對(duì)象拷貝)。

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

// 引用類(lèi)型:地址(被 User 類(lèi)引用)
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; }
}

// 實(shí)現(xiàn) Cloneable 接口,支持淺拷貝
class User implements Cloneable {
    private String name; // 基本類(lèi)型包裝類(lèi)(不可變,淺拷貝無(wú)問(wèn)題)
    private int age;     // 基本類(lèi)型(淺拷貝直接復(fù)制值)
    private Address addr;// 引用類(lèi)型(淺拷貝僅復(fù)制引用,共享對(duì)象)

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

    // 重寫(xiě) clone() 方法:實(shí)現(xiàn)淺拷貝
    @Override
    public User clone() throws CloneNotSupportedException {
        // 調(diào)用 Object 的 clone(),JVM 會(huì)創(chuàng)建新對(duì)象并復(fù)制成員值
        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; }
}

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

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

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

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

(4)淺拷貝的問(wèn)題

當(dāng)對(duì)象包含 可變的引用類(lèi)型成員(如 Address)時(shí),淺拷貝會(huì)導(dǎo)致原對(duì)象和拷貝對(duì)象共享該成員,修改其中一個(gè)會(huì)影響另一個(gè),破壞對(duì)象的 “獨(dú)立性”—— 這就是需要深拷貝的場(chǎng)景。

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

1. 深拷貝的定義

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

  • 簡(jiǎn)單說(shuō):“拷貝對(duì)象本身 + 拷貝對(duì)象里所有的‘引用子對(duì)象”。

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

方式 1:重寫(xiě)clone()方法,手動(dòng)遞歸拷貝引用類(lèi)型

核心思路:在淺拷貝的基礎(chǔ)上,對(duì)每個(gè)引用類(lèi)型成員也調(diào)用其 clone() 方法(需讓引用類(lèi)型也實(shí)現(xiàn) Cloneable)。

// 步驟1:讓引用類(lèi)型 Address 也實(shí)現(xiàn) Cloneable,支持拷貝
class Address implements Cloneable {
    private String city;

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

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

    // getter/setter 略
}

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

    // 構(gòu)造方法略

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

    // getter/setter 略
}

// 測(cè)試深拷貝
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();

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

方式 2:通過(guò)序列化(Serializable)實(shí)現(xiàn)深拷貝

核心思路:利用 Java 序列化將對(duì)象寫(xiě)入流(序列化),再?gòu)牧髦凶x?。ǚ葱蛄谢?/span>,生成的新對(duì)象是完全獨(dú)立的(無(wú)需手動(dòng)遞歸拷貝)。

  • 優(yōu)點(diǎn):無(wú)需讓每個(gè)引用類(lèi)型都實(shí)現(xiàn) Cloneable,適合復(fù)雜對(duì)象(多層引用嵌套);
  • 缺點(diǎn):需要所有成員變量都實(shí)現(xiàn) Serializable 接口,效率略低于手動(dòng)克隆。
import java.io.*;

// 步驟1:所有相關(guān)類(lèi)實(shí)現(xiàn) Serializable 接口(標(biāo)記可序列化)
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:實(shí)現(xiàn)深拷貝工具方法(序列化+反序列化)
    public User deepCopy() throws IOException, ClassNotFoundException {
        // 1. 序列化:將對(duì)象寫(xiě)入字節(jié)流
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(bos);
        oos.writeObject(this); // 寫(xiě)入當(dāng)前 User 對(duì)象

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

    // getter/setter 略
}

// 測(cè)試序列化深拷貝
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();

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

方式 3:使用第三方工具(簡(jiǎn)化開(kāi)發(fā))

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

  • Apache Commons LangSerializationUtils.clone()(基于序列化,無(wú)需手動(dòng)寫(xiě)流操作);
  • Gson/Jackson:將對(duì)象轉(zhuǎn)為 JSON 字符串,再轉(zhuǎn)回對(duì)象(間接實(shí)現(xiàn)深拷貝,支持復(fù)雜對(duì)象)。

示例(Apache Commons Lang):

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

3. 深拷貝的注意事項(xiàng)

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

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

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

1. 面試高頻問(wèn)題

(1)Cloneable接口有什么用?如果不實(shí)現(xiàn)它,調(diào)用clone()會(huì)怎樣?

  • 作用:標(biāo)記類(lèi)的對(duì)象允許被克隆(無(wú)任何方法,僅為 JVM 提供標(biāo)記);
  • 不實(shí)現(xiàn)后果:調(diào)用 super.clone() 時(shí)會(huì)拋出 CloneNotSupportedException(運(yùn)行時(shí)異常)。

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

默認(rèn)是 淺拷貝僅復(fù)制對(duì)象的成員值(基本類(lèi)型復(fù)制值,引用類(lèi)型復(fù)制引用地址)。

(3)深拷貝的實(shí)現(xiàn)方式有哪些?各自的優(yōu)缺點(diǎn)?

  • 手動(dòng)克隆:優(yōu)點(diǎn)效率高,缺點(diǎn)需遞歸實(shí)現(xiàn),復(fù)雜對(duì)象繁瑣;
  • 序列化:優(yōu)點(diǎn)無(wú)需手動(dòng)處理嵌套,缺點(diǎn)需實(shí)現(xiàn) Serializable,效率略低;
  • 第三方工具:優(yōu)點(diǎn)簡(jiǎn)潔高效,缺點(diǎn)依賴外部依賴。

(4)為什么String類(lèi)型在淺拷貝中不會(huì)有問(wèn)題?

String 是 不可變類(lèi)型(一旦創(chuàng)建無(wú)法修改,修改時(shí)會(huì)生成新 String 對(duì)象),淺拷貝時(shí)共享引用不會(huì)導(dǎo)致原對(duì)象被修改,因此無(wú)需深拷貝。

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

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

總結(jié)

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

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

相關(guān)文章

  • Java中的ArrayList(擴(kuò)容機(jī)制)詳解

    Java中的ArrayList(擴(kuò)容機(jī)制)詳解

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

    Java運(yùn)行時(shí)動(dòng)態(tài)生成類(lèi)實(shí)現(xiàn)過(guò)程詳解

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

    Java之SpringCloudAlibaba Sentinel組件案例講解

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

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

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

    JAVA用戶自定義事件監(jiān)聽(tīng)實(shí)例代碼

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

    springboot項(xiàng)目中引入本地依賴jar包并打包到lib文件夾中

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

    @Qualifier使用及詳細(xì)源碼展示

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

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

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

    Java Collection 移除元素方法及注意事項(xiàng)

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

    websocket實(shí)現(xiàn)方法日志實(shí)時(shí)查詢過(guò)程

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

最新評(píng)論

宁夏| 娄烦县| 新营市| 太白县| 虎林市| 南昌市| 罗源县| 汤原县| 靖安县| 孝义市| 平利县| 仪陇县| 大田县| 广西| 松潘县| 南安市| 新源县| 芜湖市| 广河县| 林西县| 望城县| 汉阴县| 桦甸市| 芒康县| 汉沽区| 稷山县| 凭祥市| 玉溪市| 聂荣县| 毕节市| 汨罗市| 龙山县| 舞阳县| 平度市| 邯郸市| 洛南县| 龙川县| 东宁县| 绥阳县| 宁波市| 什邡市|