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

Java基礎(chǔ)之序列化與反序列化詳解

 更新時(shí)間:2021年04月19日 15:07:57   作者:程序員孫大圣  
這篇文章主要介紹了Java基礎(chǔ)之序列化與反序列化詳解,文中有非常詳細(xì)的代碼示例,對(duì)正在學(xué)習(xí)java基礎(chǔ)的小伙伴們有很好的幫助,需要的朋友可以參考下

1、什么是序列化與反序列化?

序列化 (Serialization)是將對(duì)象的狀態(tài)信息轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)男问降倪^(guò)程。一般將一個(gè)對(duì)象存儲(chǔ)至一個(gè)儲(chǔ)存媒介,例如檔案或是記億體緩沖等。在網(wǎng)絡(luò)傳輸過(guò)程中,可以是字節(jié)或是XML等格式。而字節(jié)的或XML編碼格式可以還原完全相等的對(duì)象。這個(gè)相反的過(guò)程又稱(chēng)為反序列化。

2、Java如何實(shí)現(xiàn)序列化和反序列化?

Java對(duì)象的序列化和反序列化

在Java中,我們可以通過(guò)多種方式來(lái)創(chuàng)建對(duì)象,并且只要對(duì)象沒(méi)有被回收我們都可以復(fù)用該對(duì)象。但是,我們創(chuàng)建出來(lái)的這些Java對(duì)象都是存在于JVM的堆內(nèi)存中的。只有JVM處于運(yùn)行狀態(tài)的時(shí)候,這些對(duì)象才可能存在。一旦JVM停止運(yùn)行,這些對(duì)象的狀態(tài)也就隨之而丟失了。

但是在真實(shí)的應(yīng)用場(chǎng)景中,我們需要將這些對(duì)象持久化下來(lái),并且能夠在需要的時(shí)候把對(duì)象重新讀取出來(lái)。Java的對(duì)象序列化可以幫助我們實(shí)現(xiàn)該功能。

對(duì)象序列化機(jī)制(object serialization)是Java語(yǔ)言?xún)?nèi)建的一種對(duì)象持久化方式,通過(guò)對(duì)象序列化,可以把對(duì)象的狀態(tài)保存為字節(jié)數(shù)組,并且可以在有需要的時(shí)候?qū)⑦@個(gè)字節(jié)數(shù)組通過(guò)反序列化的方式再轉(zhuǎn)換成對(duì)象。對(duì)象序列化可以很容易的在JVM中的活動(dòng)對(duì)象和字節(jié)數(shù)組(流)之間進(jìn)行轉(zhuǎn)換。

在Java中,對(duì)象的序列化與反序列化被廣泛應(yīng)用到RMI(遠(yuǎn)程方法調(diào)用)及網(wǎng)絡(luò)傳輸中。

在Java中,只要一個(gè)類(lèi)實(shí)現(xiàn)了java.io.Serializable接口,那么它就可以被序列化。這里先來(lái)一段代碼:

code 1 創(chuàng)建一個(gè)User類(lèi),用于序列化及反序列化

public class User implements Serializable{
    private String name;
    private int age;
    private Date birthday;
    private transient String gender;
    private static final long serialVersionUID = -6849794470754667710L;
 
    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 Date getBirthday() {
        return birthday;
    }
 
    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }
 
    public String getGender() {
        return gender;
    }
 
    public void setGender(String gender) {
        this.gender = gender;
    }
 
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", gender=" + gender +
                ", birthday=" + birthday +
                '}';
    }
}

code 2 對(duì)User進(jìn)行序列化及反序列化的Demo 

public class SerializableDemo {
 
    public static void main(String[] args) {
        //Initializes The Object
        User user = new User();
        user.setName("hollis");
        user.setGender("male");
        user.setAge(23);
        user.setBirthday(new Date());
        System.out.println(user);
 
        //Write Obj to File
        ObjectOutputStream oos = null;
        try {
            oos = new ObjectOutputStream(new FileOutputStream("tempFile"));
            oos.writeObject(user);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(oos);
        }
 
        //Read Obj from File
        File file = new File("tempFile");
        ObjectInputStream ois = null;
        try {
            ois = new ObjectInputStream(new FileInputStream(file));
            User newUser = (User) ois.readObject();
            System.out.println(newUser);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            IOUtils.closeQuietly(ois);
            try {
                FileUtils.forceDelete(file);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
 
    }
}
//output 
//User{name='hollis', age=23, gender=male, birthday=Tue Feb 02 17:37:38 CST 2016}
//User{name='hollis', age=23, gender=null, birthday=Tue Feb 02 17:37:38 CST 2016}

1、在Java中,只要一個(gè)類(lèi)實(shí)現(xiàn)了java.io.Serializable接口,那么它就可以被序列化。

2、通過(guò)ObjectOutputStreamObjectInputStream對(duì)對(duì)象進(jìn)行序列化及反序列化

3、虛擬機(jī)是否允許反序列化,不僅取決于類(lèi)路徑和功能代碼是否一致,一個(gè)非常重要的一點(diǎn)是兩個(gè)類(lèi)的序列化 ID 是否一致(就是 private static final long serialVersionUID

4、序列化并不保存靜態(tài)變量。

5、要想將父類(lèi)對(duì)象也序列化,就需要讓父類(lèi)也實(shí)現(xiàn)Serializable 接口。

6、Transient 關(guān)鍵字的作用是控制變量的序列化,在變量聲明前加上該關(guān)鍵字,可以阻止該變量被序列化到文件中,在被反序列化后,transient 變量的值被設(shè)為初始值,如 int 型的是 0,對(duì)象型的是 null。

7、服務(wù)器端給客戶(hù)端發(fā)送序列化對(duì)象數(shù)據(jù),對(duì)象中有一些數(shù)據(jù)是敏感的,比如密碼字符串等,希望對(duì)該密碼字段在序列化時(shí),進(jìn)行加密,而客戶(hù)端如果擁有解密的密鑰,只有在客戶(hù)端進(jìn)行反序列化時(shí),才可以對(duì)密碼進(jìn)行讀取,這樣可以一定程度保證序列化對(duì)象的數(shù)據(jù)安全。

3、如何自定義序列化和反序列化呢?

code 3

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;
    transient Object[] elementData; // non-private to simplify nested class access
    private int size;
}

帶著這個(gè)問(wèn)題,我們來(lái)看java.util.ArrayList的源碼從上面的代碼中可以知道ArrayList實(shí)現(xiàn)了java.io.Serializable接口,那么我們就可以對(duì)它進(jìn)行序列化及反序列化。因?yàn)閑lementData是transient的,所以我們認(rèn)為這個(gè)成員變量不會(huì)被序列化而保留下來(lái)。我們寫(xiě)一個(gè)Demo,驗(yàn)證一下我們的想法:

code4

public static void main(String[] args) throws IOException, ClassNotFoundException {
        List<String> stringList = new ArrayList<String>();
        stringList.add("hello");
        stringList.add("world");
        stringList.add("hollis");
        stringList.add("chuang");
        System.out.println("init StringList" + stringList);
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("stringlist"));
        objectOutputStream.writeObject(stringList);
 
        IOUtils.close(objectOutputStream);
        File file = new File("stringlist");
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(file));
        List<String> newStringList = (List<String>)objectInputStream.readObject();
        IOUtils.close(objectInputStream);
        if(file.exists()){
            file.delete();
        }
        System.out.println("new StringList" + newStringList);
    }
//init StringList[hello, world, hollis, chuang]
//new StringList[hello, world, hollis, chuang]

了解ArrayList的人都知道,ArrayList底層是通過(guò)數(shù)組實(shí)現(xiàn)的。那么數(shù)組elementData其實(shí)就是用來(lái)保存列表中的元素的。通過(guò)該屬性的聲明方式我們知道,他是無(wú)法通過(guò)序列化持久化下來(lái)的。那么為什么code 4的結(jié)果卻通過(guò)序列化和反序列化把List中的元素保留下來(lái)了呢? 

4、writeObject和readObject方法

在ArrayList中定義了來(lái)個(gè)方法: writeObjectreadObject

這里先給出結(jié)論:

在序列化過(guò)程中,如果被序列化的類(lèi)中定義了writeObject 和 readObject 方法,虛擬機(jī)會(huì)試圖調(diào)用對(duì)象類(lèi)里的 writeObject 和 readObject 方法,進(jìn)行用戶(hù)自定義的序列化和反序列化。

如果沒(méi)有這樣的方法,則默認(rèn)調(diào)用是 ObjectOutputStream 的 defaultWriteObject 方法以及 ObjectInputStream 的 defaultReadObject 方法。

用戶(hù)自定義的 writeObject 和 readObject 方法可以允許用戶(hù)控制序列化的過(guò)程,比如可以在序列化的過(guò)程中動(dòng)態(tài)改變序列化的數(shù)值。

那么為什么ArrayList要用這種方式來(lái)實(shí)現(xiàn)序列化呢? 

ArrayList實(shí)際上是動(dòng)態(tài)數(shù)組,每次在放滿(mǎn)以后自動(dòng)增長(zhǎng)設(shè)定的長(zhǎng)度值,如果數(shù)組自動(dòng)增長(zhǎng)長(zhǎng)度設(shè)為100,而實(shí)際只放了一個(gè)元素,那就會(huì)序列化99個(gè)null元素。為了保證在序列化的時(shí)候不會(huì)將這么多null同時(shí)進(jìn)行序列化,ArrayList把元素?cái)?shù)組設(shè)置為transient。

前面說(shuō)過(guò),為了防止一個(gè)包含大量空對(duì)象的數(shù)組被序列化,為了優(yōu)化存儲(chǔ),所以,ArrayList使用transient來(lái)聲明elementData。 但是,作為一個(gè)集合,在序列化過(guò)程中還必須保證其中的元素可以被持久化下來(lái),所以,通過(guò)重寫(xiě)writeObject 和 readObject方法的方式把其中的元素保留下來(lái)。

writeObject方法把elementData數(shù)組中的元素遍歷的保存到輸出流(ObjectOutputStream)中。

readObject方法從輸入流(ObjectInputStream)中讀出對(duì)象并保存賦值到elementData數(shù)組中

至此,我們先試著來(lái)回答剛剛提出的問(wèn)題:

如何自定義的序列化和反序列化策略

答:可以通過(guò)在被序列化的類(lèi)中增加writeObject 和 readObject方法。那么問(wèn)題又來(lái)了:

雖然ArrayList中寫(xiě)了writeObject 和 readObject 方法,但是這兩個(gè)方法并沒(méi)有顯示的被調(diào)用啊。

那么如果一個(gè)類(lèi)中包含writeObject 和 readObject 方法,那么這兩個(gè)方法是怎么被調(diào)用的呢?

在使用ObjectOutputStream的writeObject方法和ObjectInputStream的readObject方法時(shí),會(huì)通過(guò)反射的方式調(diào)用。

5、serializable接口

類(lèi)通過(guò)實(shí)現(xiàn) java.io.Serializable 接口以啟用其序列化功能。未實(shí)現(xiàn)此接口的類(lèi)將無(wú)法使其任何狀態(tài)序列化或反序列化??尚蛄谢?lèi)的所有子類(lèi)型本身都是可序列化的。序列化接口沒(méi)有方法或字段,僅用于標(biāo)識(shí)可序列化的語(yǔ)義。

該接口并沒(méi)有字段和方法,為什么只有實(shí)現(xiàn)了該接口的對(duì)象才能被序列化呢?

在進(jìn)行序列化操作時(shí),會(huì)判斷要被序列化的類(lèi)是否是Enum、Array和Serializable類(lèi)型,如果不是則直接拋出NotSerializableException。

序列化ID:

private static final long serialVersionUID = 1L;

序列化 ID在 IDE 下提供了兩種生成策略,一個(gè)設(shè)為固定的 1L,另一個(gè)是隨機(jī)生成一個(gè)不重復(fù)的 long 類(lèi)型數(shù)據(jù)(實(shí)際上是使用 JDK 工具生成)。一般如果沒(méi)有特殊需求,用默認(rèn)的 1L 就可以,這樣可以確保反序列化成功。因?yàn)椴煌男蛄谢痠d之間不能進(jìn)行序列化和反序列化。

到此這篇關(guān)于Java基礎(chǔ)之序列化與反序列化詳解的文章就介紹到這了,更多相關(guān)Java序列化與反序列化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 利用Java8 Optional如何避免空指針異常詳解

    利用Java8 Optional如何避免空指針異常詳解

    Optional可以讓你的代碼具有可讀性,且會(huì)避免出現(xiàn)空指針異常。 下面這篇文章主要給大家介紹了關(guān)于利用Java8 Optional如何避免空指針異常的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下。
    2018-01-01
  • 使用Netty解決TCP粘包和拆包問(wèn)題過(guò)程詳解

    使用Netty解決TCP粘包和拆包問(wèn)題過(guò)程詳解

    這篇文章主要介紹了使用Netty解決TCP粘包和拆包問(wèn)題過(guò)程詳解,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-07-07
  • 剖析Java中的事件處理與異常處理機(jī)制

    剖析Java中的事件處理與異常處理機(jī)制

    這篇文章主要介紹了Java中的事件處理與異常處理機(jī)制,講解Java是如何對(duì)事件或者異常作出響應(yīng)以及定義異常的一些方法,需要的朋友可以參考下
    2016-01-01
  • Java8新特性之lambda的作用_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    Java8新特性之lambda的作用_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理

    我們期待了很久lambda為java帶來(lái)閉包的概念,但是如果我們不在集合中使用它的話,就損失了很大價(jià)值?,F(xiàn)有接口遷移成為lambda風(fēng)格的問(wèn)題已經(jīng)通過(guò)default methods解決了,在這篇文章將深入解析Java集合里面的批量數(shù)據(jù)操作解開(kāi)lambda最強(qiáng)作用的神秘面紗。
    2017-06-06
  • Java如何讀取csv文件并將數(shù)據(jù)放入對(duì)象中

    Java如何讀取csv文件并將數(shù)據(jù)放入對(duì)象中

    這篇文章主要介紹了Java如何讀取csv文件并將數(shù)據(jù)放入對(duì)象中的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-04-04
  • myBatis使用@GeneratedValue(generator?=?“...“,?strategy?=?...)注解

    myBatis使用@GeneratedValue(generator?=?“...“,?strategy?=?

    這篇文章主要介紹了myBatis使用@GeneratedValue(generator?=?“...“,?strategy?=?...)注解問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-07-07
  • 為什么SpringBoot的jar可以直接運(yùn)行

    為什么SpringBoot的jar可以直接運(yùn)行

    這篇文章主要介紹了為什么SpringBoot的jar可以直接運(yùn)行,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-05-05
  • Mybatis 簡(jiǎn)單啟動(dòng)過(guò)程入門(mén)詳解

    Mybatis 簡(jiǎn)單啟動(dòng)過(guò)程入門(mén)詳解

    MyBatis是一個(gè)持久層框架,簡(jiǎn)化JDBC操作,SpringBoot集成MyBatis,通過(guò)創(chuàng)建項(xiàng)目、準(zhǔn)備數(shù)據(jù)、配置文件、實(shí)體類(lèi)和接口,可以實(shí)現(xiàn)數(shù)據(jù)庫(kù)操作,使用@Mapper和@Select注解簡(jiǎn)化接口實(shí)現(xiàn),測(cè)試類(lèi)使用@SpringBootTest和@Test注解啟動(dòng),本文介紹Mybatis啟動(dòng)過(guò)程,感興趣的朋友一起看看吧
    2025-03-03
  • Java 如何將前端傳來(lái)的數(shù)字轉(zhuǎn)化為日期

    Java 如何將前端傳來(lái)的數(shù)字轉(zhuǎn)化為日期

    這篇文章主要介紹了Java 如何將前端傳來(lái)的數(shù)字轉(zhuǎn)化為日期,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-06-06
  • Java判斷當(dāng)前日期是周幾的方法匯總

    Java判斷當(dāng)前日期是周幾的方法匯總

    在Java編程中,我們經(jīng)常會(huì)遇到需要獲取當(dāng)前日期是周幾的需求。根據(jù)國(guó)際慣例,一周通常是從周一開(kāi)始,到周日結(jié)束,記作1至7,本文將介紹幾種常用的Java方法,讓你能夠準(zhǔn)確地判斷當(dāng)前日期是周幾,感興趣的朋友一起看看吧
    2024-03-03

最新評(píng)論

石河子市| 泗阳县| 修文县| 灵璧县| 商城县| 长沙县| 大丰市| 泽库县| 鄂托克旗| 江油市| 永定县| 监利县| 丹巴县| 长岭县| 宝兴县| 望谟县| 于都县| 陵川县| 乾安县| 商河县| 临清市| 南澳县| 昌平区| 来安县| 嵊泗县| 张家界市| 沾化县| 江阴市| 盐源县| 江北区| 岐山县| 武陟县| 循化| 布尔津县| 甘谷县| 安丘市| 沂南县| 江永县| 响水县| 专栏| 寻乌县|