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

詳解Java序列化機(jī)制

 更新時(shí)間:2020年12月31日 10:09:13   作者:幽暗森林之豬大屁  
這篇文章主要介紹了Java序列化機(jī)制的相關(guān)資料,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下

概況

在程序中為了能直接以 Java 對象的形式進(jìn)行保存,然后再重新得到該 Java 對象,這就需要序列化能力。序列化其實(shí)可以看成是一種機(jī)制,按照一定的格式將 Java 對象的某狀態(tài)轉(zhuǎn)成介質(zhì)可接受的形式,以方便存儲(chǔ)或傳輸。其實(shí)想想就大致清楚基本流程,序列化時(shí)將 Java 對象相關(guān)的類信息、屬性及屬性值等等保存起來,反序列化時(shí)再根據(jù)這些信息構(gòu)建出 Java 對象。而過程可能涉及到其他對象的引用,所以這里引用的對象的相關(guān)信息也要參與序列化。
Java 中進(jìn)行序列化操作需要實(shí)現(xiàn) Serializable 或 Externalizable 接口。

序列化的作用

  • 提供一種簡單又可擴(kuò)展的對象保存恢復(fù)機(jī)制。
  • 對于遠(yuǎn)程調(diào)用,能方便對對象進(jìn)行編碼和解碼,就像實(shí)現(xiàn)對象直接傳輸。
  • 可以將對象持久化到介質(zhì)中,就像實(shí)現(xiàn)對象直接存儲(chǔ)。
  • 允許對象自定義外部存儲(chǔ)的格式。

序列化例子

FileOutputStream f = new FileOutputStream("tmp.o");
   ObjectOutput s = new ObjectOutputStream(f);
   s.writeObject("test");
   s.writeObject(new ArrayList());
   s.flush();

常見的使用方式是直接將對象寫入流中,比如上述例子中,創(chuàng)建了 FileOutputStream 對象,其對應(yīng)輸出到 tmp.o 文件中,然后創(chuàng)建 ObjectOutputStream 對象嵌套前面的輸出流。當(dāng)我們調(diào)用 writeObject 方法時(shí)即能進(jìn)行序列化操作。
writeObject 方法這里需要說明下,在對某個(gè)對象進(jìn)行寫入時(shí),它其實(shí)不僅僅序列化自己,還會(huì)去遍歷尋找相關(guān)引用的其他對象,由自己和其他引用對象組成的一個(gè)完整的對象圖關(guān)系都會(huì)被序列化。
對于數(shù)組、enum、Class類對象、ObjectStreamClass 和 String 等都會(huì)做特殊處理,而其他對象序列化則需要實(shí)現(xiàn) Serializable 或 Externalizable 接口。

反序列化例子

FileInputStream in = new FileInputStream("tmp.o");
    ObjectInputStream s = new ObjectInputStream(in);
    String test = (String)s.readObject();
    List list = (ArrayList)s.readObject();

針對序列化則存在反序列化操作,通過流直接讀取對象,先創(chuàng)建 FileInputStream 對象,其對應(yīng)輸入文件為 tmp.o,然后創(chuàng)建 ObjectInputStream 對象嵌套前面的輸入流,接著則可以調(diào)用 readObject 方法讀取對象。
其中調(diào)用 readObject 方法反序列操作的過程,除了會(huì)恢復(fù)對象自己之外還會(huì)遍歷整個(gè)完整的對象圖,創(chuàng)建整個(gè)對象圖包含的所有對象。

serialVersionUID 有什么用

在序列化操作時(shí),經(jīng)常會(huì)看到實(shí)現(xiàn)了 Serializable 接口的類會(huì)存在一個(gè) serialVersionUID 屬性,并且它是一個(gè)固定數(shù)值的靜態(tài)變量。比如如下,這個(gè)屬性有什么作用?其實(shí)它主要用于驗(yàn)證版本一致性,每個(gè)類都擁有這么一個(gè) ID,在序列化的時(shí)候會(huì)一起被寫入流中,那么在反序列化的時(shí)候就被拿出來跟當(dāng)前類的 serialVersionUID 值進(jìn)行比較,兩者相同則說明版本一致,可以序列化成功,而如果不同則序列化失敗。

private static final long serialVersionUID = -6849794470754667710L;

一般情況下我們可以自己定義 serialVersionUID 的值或者 IDE 幫我們自動(dòng)生成,而如果我們不顯示定義 serialVersionUID 的話,這不代表不存在 serialVersionUID,而是由 JDK 幫我們生成,生成規(guī)則是會(huì)利用類名、類修飾符、接口名、字段、靜態(tài)初始化信息、構(gòu)造函數(shù)信息、方法名、方法修飾符、方法簽名等組成的信息,經(jīng)過 SHA 算法生成摘要即是最終的 serialVersionUID 值。

父類序列化什么情況

如果一個(gè)子類實(shí)現(xiàn)了 Serializable 接口而父類沒有實(shí)現(xiàn)該接口,則在序列化子類時(shí),子類的屬性狀態(tài)會(huì)被寫入而父類的屬性狀態(tài)將不被寫入。所以如果想要父類屬性狀態(tài)也一起參與序列化,就要讓它也實(shí)現(xiàn) Serializable 接口。
另外,如果父類未實(shí)現(xiàn) Serializable 接口則反序列化生成的對象會(huì)再次調(diào)用父類的構(gòu)造函數(shù),以此完成對父類的初始化。所以父類屬性初始值一般都是類型的默認(rèn)值。比如下面,F(xiàn)ather 類的屬性不會(huì)參與序列化,反序列化時(shí) Father 對象的屬性的值為默認(rèn)值0。

public class Father {
        public int f;
 
        public Father() {
        }
    }
 
    public class Son extends Father implements Serializable {
        public int s;
 
        public Son() {
            super();
        }
    }

哪些字段會(huì)序列化

在序列化時(shí)類的哪些字段會(huì)參與到序列化中呢?其實(shí)有兩種方式?jīng)Q定哪些字段會(huì)被序列化,
1. 默認(rèn)方式,Java對象中的非靜態(tài)和非transient的字段都會(huì)被定義為需要序列的字段。
2. 另外一種方式是通過 ObjectStreamField 數(shù)組來聲明類需要序列化的對象。
可以看到普通的字段都是默認(rèn)會(huì)被序列化的,而對于某些包含敏感信息的字段我們不希望它參與序列化,那么最簡單的方式就是可以將該字段聲明為 transient。
如何使用 ObjectStreamField?舉個(gè)例子,如下,A類中有 name 和 password 兩個(gè)字段,通過 ObjectStreamField 數(shù)組聲明只序列化 name 字段。這種聲明的方式不用糾結(jié)為什么這樣,這僅僅是約定了這樣而已。

public class A implements Serializable {
       String name;
       String password
 
       private static final ObjectStreamField[] serialPersistentFields
                    = {new ObjectStreamField("name", String.class)};
    }

枚舉類型的序列化

Enum 類型的序列化與普通的 Java 類的序列化有所不同,那么在深入之前可以先看這篇文章深入了解下枚舉,《 [從JDK角度認(rèn)識(shí)枚舉enum][JDK_enum]》。
所以我們知道枚舉被編譯后會(huì)變成一個(gè)繼承 java.lang.Enum 的類,而且枚舉里面的元素被聲明成 static final ,另外生成一個(gè)靜態(tài)代碼塊 static{},最后還會(huì)生成 values 和 valueOf 兩個(gè)方法。Enum 類是一個(gè)抽象類,主要有 name 和 ordinal 兩個(gè)屬性,分別用于表示枚舉元素的名稱和枚舉元素的位置索引。
Enum 類型參與序列化時(shí)只會(huì)將枚舉對象中的 name 屬性寫入,而其他的屬性則不參與進(jìn)來。在反序列化時(shí),則是先讀取 name 屬性,然后再通過 java.lang.Enum 類的 valueOf 方法找到對應(yīng)的枚舉類型。
除此之外,不能自定義 Enum 類型的序列化,所以 writeObject, readObject, readObjectNoData, writeReplace 以及 readResolve 等方法在序列化時(shí)會(huì)被忽略,類似的,serialPersistentFields 和 serialVersionUID 屬性都會(huì)被忽略。
最后,在序列化場景中,涉及到使用枚舉的情況時(shí)要仔細(xì)設(shè)計(jì)好,不然很可能會(huì)因?yàn)楹竺嫔壭薷牧嗣杜e類的結(jié)構(gòu)而導(dǎo)致反序列化失敗。

Externalizable 接口作用

Externalizable 接口主要就是提供給用戶自己控制序列化內(nèi)容,雖然前面我們也看到了 transient 和 ObjectStreamField 能定義序列化的字段,但通過 Externalizable 接口則能更加靈活。可以看到它其實(shí)繼承了 Serializable 接口,提供了 writeExternal 和 readExternal 兩個(gè)方法,也就是在這兩個(gè)方法內(nèi)控制序列化和反序列化的內(nèi)容。

public interface Externalizable extends java.io.Serializable {
 
        void writeExternal(ObjectOutput out) throws IOException;
 
        void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
    }

比如下面的例子,我們可以在 writeExternal 方法中額外寫入 Date 對象,然后再寫入 value 值。對應(yīng)的,反序列化時(shí)則是在 readExternal 方法中讀取 Date 對象和 value。這樣就完成了自定義序列化操作。

public class ExternalizableTest implements Externalizable {
        public String value = "test";
 
        public ExternalizableTest() {
        }
 
        public void writeExternal(ObjectOutput out) throws IOException {
            Date d = new Date();
            out.writeObject(d);
            out.writeObject(value);
        }
 
        public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
            Date d = (Date) in.readObject();
            System.out.println(d);
            System.out.println((String) in.readObject());
        }
 
    }

寫入時(shí)替換對象

正常情況下序列化某個(gè)對象時(shí)寫入的正是當(dāng)前的對象,但如果說我們要替換當(dāng)前的對象而寫入其他對象的話則可以通過 writeReplace 方法來實(shí)現(xiàn)。比如下面,person 類通過 writeReplace 方法最終可以寫入 Object 數(shù)組對象。所以我們在反序列化時(shí)就不再是轉(zhuǎn)換成 Person 類型,而是要轉(zhuǎn)換為 Object 數(shù)組對象。

class Person implements Serializable {
        private String name;
        private int age;
 
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
 
        private Object writeReplace() throws ObjectStreamException {
            Object[] properties = new Object[2];
            properties[0] = name;
            properties[1] = age;
            return properties;
        }
    }
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.o"));
   Object[] properties = (Object[]) ois.readObject();

讀取時(shí)替換對象

上面介紹了在寫入時(shí)可以替換對象,而在讀取時(shí)也同樣支持替換對象的,它是通過 readResolve 方法實(shí)現(xiàn)的。比如下面,在 readResolve 方法返回 2222,則反序列化讀取時(shí)不再是 Person 對象,而是 2222。

class Person implements Serializable {
        private String name;
        private int age;
 
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
 
        private Object readResolve() throws ObjectStreamException {
            return 2222;
        }
    }
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test.o"));
    Object o = ois.readObject();

以上就是詳解Java序列化機(jī)制的詳細(xì)內(nèi)容,更多關(guān)于Java序列化機(jī)制的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java GZIP壓縮與解壓縮代碼實(shí)例

    Java GZIP壓縮與解壓縮代碼實(shí)例

    這篇文章主要介紹了Java GZIP壓縮與解壓縮代碼實(shí)例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2020-01-01
  • Java大數(shù)字運(yùn)算之BigInteger

    Java大數(shù)字運(yùn)算之BigInteger

    在Java中提供了大數(shù)字的操作類,即 java.math.BigInteger 類與 java.math.BigDecimal 類。其中,BigInteger 類是針對大整數(shù)的處理類,這里有Integer 類的解釋,使用方法和實(shí)例,需要的朋友可以參考下。
    2017-08-08
  • spring boot 不連接數(shù)據(jù)庫啟動(dòng)的解決

    spring boot 不連接數(shù)據(jù)庫啟動(dòng)的解決

    這篇文章主要介紹了spring boot 不連接數(shù)據(jù)庫啟動(dòng)的解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • JDK1.8中ConcurrentHashMap中computeIfAbsent死循環(huán)bug問題

    JDK1.8中ConcurrentHashMap中computeIfAbsent死循環(huán)bug問題

    這篇文章主要介紹了JDK1.8中ConcurrentHashMap中computeIfAbsent死循環(huán)bug,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • 解決異常:Invalid?keystore?format,springboot配置ssl證書格式不合法問題

    解決異常:Invalid?keystore?format,springboot配置ssl證書格式不合法問題

    這篇文章主要介紹了解決異常:Invalid?keystore?format,springboot配置ssl證書格式不合法問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2024-03-03
  • 詳解Java如何實(shí)現(xiàn)在PDF中插入,替換或刪除圖像

    詳解Java如何實(shí)現(xiàn)在PDF中插入,替換或刪除圖像

    圖文并茂的內(nèi)容往往讓人看起來更加舒服,如果只是文字內(nèi)容的累加,往往會(huì)使讀者產(chǎn)生視覺疲勞。搭配精美的文章配圖則會(huì)使文章內(nèi)容更加豐富。那我們要如何在PDF中插入、替換或刪除圖像呢?別擔(dān)心,今天為大家介紹一種高效便捷的方法
    2023-01-01
  • Java線程讓步y(tǒng)ield用法實(shí)例分析

    Java線程讓步y(tǒng)ield用法實(shí)例分析

    這篇文章主要介紹了Java線程讓步y(tǒng)ield用法,結(jié)合實(shí)例形式分析了java中yield()方法的功能、原理及線程讓步操作的相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2019-09-09
  • Java dom4j生成與解析XML案例詳解

    Java dom4j生成與解析XML案例詳解

    這篇文章主要介紹了Java dom4j生成與解析XML案例詳解,本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-08-08
  • 使用mybatis攔截器處理敏感字段

    使用mybatis攔截器處理敏感字段

    這篇文章主要介紹了mybatis攔截器處理敏感字段方式,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • MyBatis之insert主鍵自增和自定義主鍵詳解

    MyBatis之insert主鍵自增和自定義主鍵詳解

    本文介紹了如何使用MyBatis解決插入數(shù)據(jù)時(shí)因主鍵唯一性約束導(dǎo)致的錯(cuò)誤問題,以及如何自定義主鍵生成規(guī)則,文中詳細(xì)解釋了如何在MyBatis中配置自增主鍵,并提供了測試示例
    2024-12-12

最新評論

通海县| 土默特右旗| 海原县| 盐城市| 禹城市| 青岛市| 泰安市| 礼泉县| 古交市| 三穗县| 闸北区| 连城县| 高清| 天等县| 内江市| 攀枝花市| 广水市| 张北县| 汪清县| 仁化县| 布拖县| 平舆县| 华安县| 安泽县| 汨罗市| 南宁市| 连平县| 灯塔市| 介休市| 鄢陵县| 永城市| 赫章县| 高要市| 泸溪县| 枣庄市| 遵义市| 古浪县| 屏东县| 巴林左旗| 漳州市| 衡阳市|