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

圖文詳解Java中的序列化機(jī)制

 更新時(shí)間:2022年10月04日 09:14:21   作者:JAVA旭陽  
java中的序列化可能大家像我一樣都停留在實(shí)現(xiàn)Serializable接口上,對于它里面的一些核心機(jī)制沒有深入了解過。本文將通過示例帶大家深入了解Java中的序列化機(jī)制,需要的可以參考一下

概述

java中的序列化可能大家像我一樣都停留在實(shí)現(xiàn)Serializable接口上,對于它里面的一些核心機(jī)制沒有深入了解過。直到最近在項(xiàng)目中踩了一個(gè)坑,就是序列化對象添加一個(gè)字段以后,使用方系統(tǒng)報(bào)了反序列化失敗,原因是我們雙方的序列化對象沒有加上serialVersionUID,那你們知道下面幾個(gè)問題嗎:

  • 序列化對象中的serialVersionUID 是干嘛用的?
  • 如何修改默認(rèn)的序列化機(jī)制?
  • 如何使用序列化的方式克隆對象?

對象序列化和反序列化機(jī)制

序列化: 將對象轉(zhuǎn)成二進(jìn)制寫到輸出流的過程。

反序列化: 通過輸入流讀回二進(jìn)制轉(zhuǎn)成對象的過程。

通過對象的序列化和反序列化機(jī)制可以實(shí)現(xiàn)對象在網(wǎng)絡(luò)之間傳輸。

在Java中,如果一個(gè)對象要想實(shí)現(xiàn)序列化,必須要實(shí)現(xiàn)下面兩個(gè)接口之一:

  • Serializable 接口
  • Externalizable 接口

這里我們先講解常用的Serializable 接口。

writeObject序列化過程栗子:

@Test
public void testSerializable() throws FileNotFoundException {
    User user = new User("alvin", 19);
    // 文件輸出流
    FileOutputStream bout = new FileOutputStream("user.dat");
    try (ObjectOutputStream out = new ObjectOutputStream(bout)) {
        // 序列化
        out.writeObject(user);
    } catch (IOException e) {
        e.printStackTrace();
    }
}


@Data
@NoArgsConstructor
@AllArgsConstructor
public class User implements Serializable {

    private String username;

    private Integer age;
}

結(jié)果:

readObject反序列化栗子:

現(xiàn)在模擬另外一個(gè)系統(tǒng)需要反序列化user.dat

@Test
public void testDeSerializable() throws FileNotFoundException {
    User user = null;
    // 寫到內(nèi)存中,當(dāng)然也可以寫到文件中
    FileInputStream fis = new FileInputStream("user.dat");
    try (ObjectInputStream in = new ObjectInputStream(fis)) {
        // 反序列化 readObject
        user = (User) in.readObject();
    } catch (IOException | ClassNotFoundException e) {
        e.printStackTrace();
    }

    Assert.assertEquals("alvin", user.getUsername());
}

如果User類不實(shí)現(xiàn)Serializable接口, 那會(huì)怎么樣?

當(dāng)然是報(bào)錯(cuò)了,如下圖:

小結(jié):

一個(gè)對象想要被序列化,那么它的類就要實(shí)現(xiàn)此接口或者它的子接口。

修改默認(rèn)的序列化機(jī)制

默認(rèn)的情況下,如果實(shí)現(xiàn)了Serializable接口的對象進(jìn)行序列化的時(shí)候,默認(rèn)會(huì)將全部的數(shù)據(jù)域,也就是成員變量進(jìn)行序列化輸出,那往往有時(shí)候并不需要這樣,有什么方法可以修改序列化機(jī)制呢?下面提供3中方式。

使用transient關(guān)鍵字

將成員變量標(biāo)記成transient,那么在序列化的過程中這些數(shù)據(jù)域會(huì)被跳過,如下圖所示:

這是一種最簡單的方式,但是不夠靈活。

自定義readObject、writeObject方法

序列化類中可以通過定義下面簽名的方法:

  • private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException
  • private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException

只要類中有這兩個(gè)簽名的方法,那么就不會(huì)調(diào)用默認(rèn)的序列化,取而代之調(diào)用這些方法。

本例我們舉個(gè)jdk中的例子,ArrayList就實(shí)現(xiàn)了這兩個(gè)方法,重寫了序列化機(jī)制。

主要原因ArrayList底層的數(shù)組通常會(huì)預(yù)留一些容量,等容量不足時(shí)再擴(kuò)充容量,那么有些空間可能就沒有實(shí)際存儲(chǔ)元素,采用自定義方式實(shí)現(xiàn)序列化時(shí),就可以保證只序列化實(shí)際存儲(chǔ)的那些元素,而不是整個(gè)數(shù)組,從而節(jié)省空間和時(shí)間。

實(shí)現(xiàn)Externalizable接口

Externalizable接口想必大家很少用到,它是Serializable接口的子類,用戶要實(shí)現(xiàn)的writeExternal()和readExternal() 方法,用來決定如何序列化和反序列化。

因?yàn)樾蛄谢头葱蛄谢椒ㄐ枰约簩?shí)現(xiàn),因此可以指定序列化哪些屬性,而transient在這里無效。

對Externalizable對象反序列化時(shí),會(huì)先調(diào)用類的無參構(gòu)造方法,這是有別于默認(rèn)反序列方式的。如果把類的不帶參數(shù)的構(gòu)造方法刪除,或者把該構(gòu)造方法的訪問權(quán)限設(shè)置為private、默認(rèn)或protected級(jí)別,會(huì)拋出java.io.InvalidException: no valid constructor異常,因此Externalizable對象必須有默認(rèn)構(gòu)造函數(shù),而且必需是public的。

舉例說明:

public class User2 implements Externalizable {

    private String username;

    private Integer age;

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeUTF(username);
        out.writeInt(age);
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
        username = in.readUTF();
        age = in.readInt();
    }
}

serialVersionUID的作用

這就回到概述中提到的項(xiàng)目中遇到的問題,現(xiàn)在簡要描述下:

A系統(tǒng)中的序列化對象User用的最新版本如下:

B系統(tǒng)中反序列化的對象,還是老的User版本如下:

這時(shí)候A系統(tǒng)生成的序列化文件,交給B系統(tǒng)反序列化時(shí),出錯(cuò)了, 如下圖:

原因:

類定義發(fā)生了變化,比如添加、刪除、修改類中的數(shù)據(jù)域后,它的唯一標(biāo)記符或者稱為SHA指紋、或者理解為serialVersionUID都會(huì)發(fā)生變化,這個(gè)值會(huì)保存在序列化二進(jìn)制中,如果反序列化過程發(fā)現(xiàn)對不上,就會(huì)報(bào)錯(cuò),如上圖所示。

那么如何處理呢?

這時(shí)候,我們?nèi)绻X得這個(gè)序列化對象是可以兼容的,那么可以自定義一個(gè)serialVersionUID的靜態(tài)成員變量,它就不會(huì)自動(dòng)生成,而是直接用這個(gè)值,如下圖:

使用序列化clone

clone大家都知道吧,在深拷貝的時(shí)候編碼還是很麻煩的,借用序列化機(jī)制可以實(shí)現(xiàn)深拷貝。做法很簡單,就是將對象序列化到輸出流中,然后讀回。

public class SerialCloneable implements Cloneable, Serializable {

    @Override
    public Object clone() throws CloneNotSupportedException {
        try {
            // 保存到字節(jié)數(shù)組流
            ByteArrayOutputStream bout = new ByteArrayOutputStream();
            try(ObjectOutputStream out = new ObjectOutputStream(bout)) {
                out.writeObject(this);
            }
            // 讀取
            try(InputStream bin = new ByteArrayInputStream(bout.toByteArray())) {
                ObjectInputStream in = new ObjectInputStream(bin);
                return in.readObject();
            }
        } catch (IOException | ClassNotFoundException e) {
            CloneNotSupportedException e2 = new CloneNotSupportedException();
            e2.initCause(e);
            throw e2;
        }
    }
}

注意一點(diǎn),這種方式性能不高,通常比顯示構(gòu)建、復(fù)制數(shù)據(jù)要慢不少。

到此這篇關(guān)于圖文詳解Java中的序列化機(jī)制的文章就介紹到這了,更多相關(guān)Java序列化機(jī)制內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Prometheus監(jiān)控Springboot程序的實(shí)現(xiàn)方法

    Prometheus監(jiān)控Springboot程序的實(shí)現(xiàn)方法

    這篇文章主要介紹了Prometheus監(jiān)控Springboot程序的實(shí)現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • SpringBoot Actuator未授權(quán)訪問漏洞修復(fù)詳解

    SpringBoot Actuator未授權(quán)訪問漏洞修復(fù)詳解

    這篇文章主要介紹了SpringBoot Actuator未授權(quán)訪問漏洞修復(fù)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • 基于java SSM springboot實(shí)現(xiàn)抗疫物質(zhì)信息管理系統(tǒng)

    基于java SSM springboot實(shí)現(xiàn)抗疫物質(zhì)信息管理系統(tǒng)

    這篇文章主要介紹了基于JAVA SSM springboot實(shí)現(xiàn)的抗疫物質(zhì)信息管理系統(tǒng),本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2021-08-08
  • eclipse實(shí)現(xiàn)可認(rèn)證的DH密鑰交換協(xié)議

    eclipse實(shí)現(xiàn)可認(rèn)證的DH密鑰交換協(xié)議

    這篇文章主要介紹了eclipse實(shí)現(xiàn)可認(rèn)證的DH密鑰交換協(xié)議,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-06-06
  • 使用spring配置文件.xml的頭文件

    使用spring配置文件.xml的頭文件

    這篇文章主要介紹了使用spring配置文件.xml的頭文件問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • 淺談springBoot注解大全

    淺談springBoot注解大全

    本篇文章主要介紹了淺談springBoot注解大全,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-03-03
  • springboot集成mybatis實(shí)例代碼

    springboot集成mybatis實(shí)例代碼

    本篇文章主要介紹了springboot集成mybatis實(shí)例代碼,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-04-04
  • 詳解Spring 兩種注入的方式(Set和構(gòu)造)實(shí)例

    詳解Spring 兩種注入的方式(Set和構(gòu)造)實(shí)例

    本篇文章主要介紹了Spring 兩種注入的方式(Set和構(gòu)造)實(shí)例,Spring框架主要提供了Set注入和構(gòu)造注入兩種依賴注入方式。有興趣的可以了解一下。
    2017-02-02
  • SpringBoot中如何對actuator進(jìn)行關(guān)閉

    SpringBoot中如何對actuator進(jìn)行關(guān)閉

    這篇文章主要介紹了SpringBoot中如何對actuator進(jìn)行關(guān)閉問題,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-03-03
  • java實(shí)現(xiàn)的新浪微博分享代碼實(shí)例

    java實(shí)現(xiàn)的新浪微博分享代碼實(shí)例

    這篇文章主要介紹了java實(shí)現(xiàn)的新浪微博分享代碼實(shí)例,是通過新浪API獲得授權(quán),然后接受客戶端請求的數(shù)據(jù),第三方應(yīng)用發(fā)送請求消息到微博,喚起微博分享界面,非常的實(shí)用,有相同需要的小伙伴可以參考下。
    2015-03-03

最新評論

大城县| 柳河县| 临猗县| 阿鲁科尔沁旗| 云和县| 延吉市| 加查县| 资源县| 新营市| 十堰市| 家居| 三门峡市| 泸西县| 辽阳市| 华安县| 大方县| 托里县| 吴桥县| 婺源县| 麻栗坡县| 开鲁县| 宿松县| 长垣县| 怀化市| 治县。| 开封县| 定陶县| 庆云县| 望都县| 盱眙县| 沧源| 且末县| 裕民县| 建始县| 安新县| 富平县| 吴川市| 柳河县| 忻城县| 正蓝旗| 安化县|