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

Java中實(shí)現(xiàn)對(duì)象的拷貝案例講解

 更新時(shí)間:2025年09月23日 10:33:19   作者:側(cè)耳傾聽111  
Java對(duì)象拷貝分為淺拷貝(復(fù)制值及引用地址)和深拷貝(遞歸復(fù)制所有引用對(duì)象),常用方法包括Object.clone()、序列化及JSON轉(zhuǎn)換,需處理循環(huán)引用問題,本文給大家介紹Java中實(shí)現(xiàn)對(duì)象的拷貝,感興趣的朋友一起看看吧

對(duì)象的拷貝

簡介

對(duì)象的拷貝,把一個(gè)對(duì)象中的數(shù)據(jù),復(fù)制到另一個(gè)相同類型的對(duì)象中,在某些情況下,這會(huì)很有用,例如,創(chuàng)建線程安全的實(shí)例中提到的一個(gè)規(guī)范,對(duì)象應(yīng)該返回自身數(shù)據(jù)的拷貝,而不應(yīng)該返回?cái)?shù)據(jù)本身,避免外部對(duì)于數(shù)據(jù)的修改影響到對(duì)象本身,String類就是這么寫的,它會(huì)對(duì)外返回char數(shù)組的拷貝,而不是char數(shù)組本身,這樣,當(dāng)用戶修改char數(shù)組時(shí),不會(huì)影響到String對(duì)象本身。

String類的toCharArray方法,返回內(nèi)部char數(shù)組的拷貝

public char[] toCharArray() {
    // Cannot use Arrays.copyOf because of class initialization order issues
    char result[] = new char[value.length];
    System.arraycopy(value, 0, result, 0, value.length);
    return result;
}

淺拷貝和深拷貝

對(duì)象的拷貝,依據(jù)類型的不同,可以分為淺拷貝和深拷貝,淺拷貝是引用復(fù)制,深拷貝是數(shù)據(jù)復(fù)制。

淺拷貝和深拷貝:

  • 淺拷貝:將原對(duì)象中的所有字段復(fù)制到新對(duì)象中,如果字段是基本數(shù)據(jù)類型,則復(fù)制的是值,如果字段是引用數(shù)據(jù)類型,則復(fù)制的是指向引用的地址,此時(shí),新對(duì)象和原對(duì)象之間存在共享數(shù)據(jù),如果某個(gè)對(duì)象修改了這部分?jǐn)?shù)據(jù),另一個(gè)對(duì)象也會(huì)受影響
  • 深拷貝:不僅將源對(duì)象中所有字段復(fù)制到新對(duì)象中,還會(huì)遞歸地復(fù)制字段引用的對(duì)象,在深拷貝結(jié)束后,原對(duì)象和新對(duì)象之間不存在任何共享數(shù)據(jù),某個(gè)對(duì)象被修改,不會(huì)影響到另一個(gè)對(duì)象。

淺拷貝

Object.clone方法,淺拷貝通常是通過這個(gè)方法來實(shí)現(xiàn),它是一個(gè)本地方法,位于Object類中,子類也可以重寫它來實(shí)現(xiàn)深拷貝,但是通常不需要這么做,因?yàn)榇鷥r(jià)比較大,如果希望深拷貝是一項(xiàng)通用能力的話。

方法簽名:protected native Object clone() throws CloneNotSupportedException;

clone方法的使用:

  • clone()方法是protected的,這意味著只有子類和同一個(gè)包中的其他類可以調(diào)用它。
  • 如果一個(gè)類沒有實(shí)現(xiàn)Cloneable接口,嘗試調(diào)用它的clone()方法將拋出CloneNotSupportedException。
  • 當(dāng)用需要克隆一個(gè)對(duì)象時(shí),需要會(huì)在子類中重寫clone()方法,并確保它是public的,這樣外部代碼才能調(diào)用它。

clone方法的底層原理:clone()方法創(chuàng)建了一個(gè)對(duì)象的淺拷貝。這意味著新對(duì)象和原始對(duì)象在內(nèi)存中是兩個(gè)獨(dú)立的副本,但是它們中的引用字段指向相同的對(duì)象。

案例:

// Person類
public class Person implements Cloneable{
    private String name;
    private int age;
    @Override
    public Person clone() throws CloneNotSupportedException {
        return (Person) super.clone();
    }
    // 省略構(gòu)造方法、getter、setter、toString
}
// PersonGroup類:持有Person類的引用,通過這個(gè)引用,就可以看出深拷貝和淺拷貝的區(qū)別
public class PersonGroup implements Cloneable {
    private String groupName;
    private Person person1;
    @Override
    public PersonGroup clone() throws CloneNotSupportedException {
        return (PersonGroup) super.clone();
    }
    // 省略構(gòu)造方法、getter、setter、toString
}
// 測試
public class Test3 {
    public static void main(String[] args) throws CloneNotSupportedException {
        Person person = new Person("張三", 18);
        PersonGroup personGroup = new PersonGroup("aaa", person);
        PersonGroup clone = personGroup.clone();
        System.out.println("clone = " + clone);
    }
}

對(duì)測試類進(jìn)行debug,觀察克隆出的實(shí)例和原實(shí)例的內(nèi)存地址,以及它們中成員變量的內(nèi)存地址,可以得出結(jié)論:Object類中的clone方法,將原實(shí)例在堆內(nèi)存中的數(shù)據(jù)復(fù)制了一份,到新的內(nèi)存空間,再將新內(nèi)存空間的地址賦值給clone后的對(duì)象,兩塊內(nèi)存地址中如果有引用類型的數(shù)據(jù),那么它們指向相同的內(nèi)存空間

關(guān)于淺拷貝,有一個(gè)地方值得注意,在原對(duì)象中有引用類型的數(shù)據(jù)的情況下,淺拷貝出的新對(duì)象,如果修改了這個(gè)引用指向的對(duì)象,原對(duì)象也會(huì)受影響,如果是修改了引用本身的指向,那么新對(duì)象不受影響。

深拷貝

深拷貝有多種實(shí)現(xiàn)方式,包括基于IO流的深拷貝、基于json轉(zhuǎn)換的深拷貝,核心原理都是把原對(duì)象和它引用的對(duì)象,全部復(fù)制一份,然后創(chuàng)建一個(gè)新對(duì)象,新對(duì)象的引用也是新對(duì)象。原對(duì)象和新對(duì)象中不存在任何共享對(duì)象。

方式1:使用IO流實(shí)現(xiàn)深拷貝

@SuppressWarnings("unchecked")
public static <T> T deepCopyByIO(T object) {
    if (object == null) {
        return null;
    }
    // ByteArrayOutputStream、ByteArrayInputStream,是內(nèi)存流,即使不關(guān)閉也不會(huì)造成資源泄露,例如文件句柄未
    // 釋放等問題,即使如此,隨時(shí)關(guān)閉流依然是一個(gè)好習(xí)慣,而且關(guān)閉輸出流會(huì)保證緩沖區(qū)的數(shù)據(jù)被正確的寫出
    try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream)) {
        objectOutputStream.writeObject(object);
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
             ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream)) {
            return (T) objectInputStream.readObject();
        }
    } catch (Exception e) {
        e.printStackTrace(); // 打印異常信息
        return null;
    }
}

apache提供的common-langs包中也提供了類似的實(shí)現(xiàn),SerializationUtils.clone方法

2、使用json實(shí)現(xiàn)深拷貝

// 這里使用的是gson框架,
public static <T> T deepCopyByGson(T object) {
    if (object == null) {
        return null;
    }
    try {
        Gson gson = new Gson(); 
        // 獲取對(duì)象的類型
        Type type = object.getClass();
        // 將對(duì)象轉(zhuǎn)換為 JSON 字符串
        String jsonString = gson.toJson(object);
        // 從 JSON 字符串解析回對(duì)象
        return gson.fromJson(jsonString, type);
    } catch (Exception e) {
        e.printStackTrace();
        return null;
    }
}

3、使用一些更高效的序列化框架,如hession、kryo等,都可以實(shí)現(xiàn)深拷貝。

深拷貝和循環(huán)引用

循環(huán)引用是指,兩個(gè)對(duì)象互相持有對(duì)方的引用,這種情況下,即使是簡單的toString方法,如果處理不當(dāng),也可能會(huì)棧內(nèi)存溢出。

案例:循環(huán)引用

// 兩個(gè)對(duì)象互相持有對(duì)方的引用
@Getter
@Setter
public static class Person1 implements Serializable {
    private String name;
    private Person2 person2;
    @Override
    public String toString() {
        return "Person1{" +
                "name='" + name + '\'' +
                ", person2=" + person2 +
                '}';
    }
}
@Getter
@Setter
public static class Person2 implements Serializable {
    private String name;
    private Person1 person1;
    @Override
    public String toString() {
        return "Person2{" +
                "name='" + name + '\'' +
                ", person1=" + person1.toString() +
                '}';
    }
}
// 測試
@Test
public void test4() {
    Person1 person1 = new Person1();
    Person2 person2 = new Person2();
    person1.setName("zs");
    person1.setPerson2(person2);
    person2.setName("ls");
    person2.setPerson1(person1);
    System.out.println("person1 = " + person1);
}

這個(gè)案例會(huì)報(bào)棧內(nèi)存溢出,因?yàn)樵诖蛴?duì)象時(shí),調(diào)用了它的toString方法,打印對(duì)象1的時(shí)候發(fā)現(xiàn)需要打印對(duì)象2,打印對(duì)象2的時(shí)候發(fā)現(xiàn)需要打印對(duì)象1,造成循環(huán)調(diào)用,最終棧內(nèi)存溢出。

所以在深拷貝時(shí),如何解決循環(huán)引用的問題?

方式1:使用基于io流的方式,這種方法天然可以避免循環(huán)引用,因?yàn)閷?duì)象序列化機(jī)制內(nèi)置了對(duì)循環(huán)引用的檢測和處理

方式2:忽略循環(huán)引用相關(guān)的字段,某些json框架可以手動(dòng)配置,忽略某些字段。

案例:使用fastjson來解決循環(huán)引用的問題,這是fastjson內(nèi)置的特性,在最終結(jié)果中,它會(huì)使用 “ref” 來代替循環(huán)引用

@Test
public void test4() {
    Person1 person1 = new Person1();
    Person2 person2 = new Person2();
    person1.setName("zs");
    person1.setPerson2(person2);
    person2.setName("ls");
    person2.setPerson1(person1);
    // 深拷貝
    String jsonString = JSON.toJSONString(person1, JSONWriter.Feature.ReferenceDetection);
    Person1 person3 = JSON.parseObject(jsonString, Person1.class);
    // {"name":"zs","person2":{"name":"ls","person1":{"$ref":"$"}}}
    System.out.println("jsonString = " + jsonString);
}

總結(jié)

這里介紹了復(fù)制一個(gè)對(duì)象的方法,包括淺拷貝、深拷貝,以及它們的基本使用

到此這篇關(guān)于Java中如何實(shí)現(xiàn)對(duì)象的拷貝的文章就介紹到這了,更多相關(guān)java對(duì)象的拷貝內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • SpringBoot集成redis實(shí)現(xiàn)共享存儲(chǔ)session

    SpringBoot集成redis實(shí)現(xiàn)共享存儲(chǔ)session

    這篇文章主要介紹了SpringBoot集成redis實(shí)現(xiàn)共享存儲(chǔ)session的流程步驟,文中通過代碼示例介紹的非常詳細(xì),并總結(jié)了一些常見的錯(cuò)誤及解決方法,需要的朋友可以參考下
    2024-03-03
  • 解決Thymeleaf中onclick的坑

    解決Thymeleaf中onclick的坑

    Thymeleaf中onclick屬性無法直接傳遞字符串參數(shù),需改用data-*屬性存儲(chǔ),再通過JS讀取,避免模板解析錯(cuò)誤,但可能仍有警告
    2025-08-08
  • java Long類型轉(zhuǎn)為String類型的兩種方式及區(qū)別說明

    java Long類型轉(zhuǎn)為String類型的兩種方式及區(qū)別說明

    這篇文章主要介紹了java Long類型轉(zhuǎn)為String類型的兩種方式及區(qū)別說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • 探究Android系統(tǒng)中解析JSON數(shù)據(jù)的方式

    探究Android系統(tǒng)中解析JSON數(shù)據(jù)的方式

    這篇文章主要介紹了探究Android系統(tǒng)中解析JSON數(shù)據(jù)的方式,文中講到了使用Java代碼實(shí)現(xiàn)的處理JSON的一些主要方法,需要的朋友可以參考下
    2015-07-07
  • IDEA2021.2永久激活碼最新超詳細(xì)(激活到2099)

    IDEA2021.2永久激活碼最新超詳細(xì)(激活到2099)

    這篇文章主要介紹了IDEA2021.2永久激活碼,是idea2021版最新激活方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-09-09
  • Java中使用Hutool的DsFactory操作多數(shù)據(jù)源的實(shí)現(xiàn)

    Java中使用Hutool的DsFactory操作多數(shù)據(jù)源的實(shí)現(xiàn)

    在Java開發(fā)中,管理多個(gè)數(shù)據(jù)源是一項(xiàng)常見需求,Hutool作為一個(gè)全能的Java工具類庫,提供了DsFactory工具,幫助開發(fā)者便捷地操作多數(shù)據(jù)源,感興趣的可以了解一下
    2024-09-09
  • java?Comparable和Comparator的區(qū)別及作用面試精講

    java?Comparable和Comparator的區(qū)別及作用面試精講

    這篇文章主要為大家介紹了java?Comparable和Comparator的區(qū)別及作用面試精講,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • 基于Java解決華為機(jī)試之字符串加解密?

    基于Java解決華為機(jī)試之字符串加解密?

    這篇文章主要介紹了基于Java解決華為機(jī)試之字符串加解密,問題描述展開主題即詳細(xì)代碼的分享完成文章內(nèi)容,具有一的的參考價(jià)值,需要的小伙伴可以參考一下。希望對(duì)你有所幫助
    2022-02-02
  • Java的包裝類特性總結(jié)

    Java的包裝類特性總結(jié)

    這篇文章主要介紹Java的包裝類的一些特性,包裝類的作用,哪些類屬于包裝類等,文中有詳細(xì)的代碼示例,對(duì)我們的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下
    2023-05-05
  • JAVA Map架構(gòu)和API介紹

    JAVA Map架構(gòu)和API介紹

    JAVA Map架構(gòu)和API介紹:Map、Map.Entry、AbstractMap、SortedMap、 NavigableMap、Dictionary。
    2013-11-11

最新評(píng)論

从江县| 塘沽区| 扎兰屯市| 盘锦市| 汉中市| 内丘县| 新干县| 涟水县| 财经| 河源市| 宣威市| 波密县| 香河县| 乐清市| 石狮市| 吉安县| 景谷| 芮城县| 邯郸县| 北辰区| 凤山县| 柳江县| 团风县| 涡阳县| 鄂伦春自治旗| 光山县| 增城市| 漳浦县| 甘洛县| 义马市| 阿城市| 井冈山市| 伊吾县| 都江堰市| 都兰县| 万宁市| 绍兴县| 清河县| 桦川县| 镇雄县| 乐平市|