Java基礎鞏固-關于Java序列化和反序列化

題外話:
從事IT要學習的東西太多了,有時候會比較浮躁,因為要學的東西太多但又無從下手,甚至有很多基礎都還沒有深入學習,這個時候應當靜下心來,正所謂不忘初心,方能始終,之前一直聽說過序列化,但也沒有去深入一點點的了解過,這個時候,就當好好鞏固下了~

java序列化

常被稱為持久化,將其寫入磁盤中。
對于一個存在于jvm的對象來說,內(nèi)部的狀態(tài)保存在內(nèi)存中,當jvm停止時這些狀態(tài)就丟失了,但有些時候?qū)ο蟮膬?nèi)部是需要持久保存的,對象序列化機制(object serialization)是Java語言內(nèi)建的一種對象持久化方式,可以很容易的在JVM中的活動對象和字節(jié)數(shù)組(流)之間進行轉(zhuǎn)換,該機制中對象可以表示為字節(jié)序列,該字節(jié)序列包括該對象的數(shù)據(jù),有關對象的類型的信息和存儲在對象中數(shù)據(jù)的類型。

數(shù)據(jù)序列化就是將對象或者數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)化成特定的格式,使其可在網(wǎng)絡中傳輸,或者可存儲在內(nèi)存或者文件中。反序列化則是相反的操作,將對象從序列化數(shù)據(jù)中還原出來。而對象序列化后的數(shù)據(jù)格式可以是二進制,可以是XML,也可以是JSON等任何格式。

【整個過程在jvm獨立的,在一個平臺上序列化的對象可以在另外的平臺反序列化】

java類序列化的條件:

1.該類必須實現(xiàn) java.io.Serializable接口。
2.該類的所有屬性必須是可序列化的。如果有一個屬性不是可序列化的,則該屬性必須注明是短暫的。如果你想知道一個 Java 標準類是否是可序列化的,請查看該類的文檔。檢驗一個類的實例是否能序列化十分簡單, 只需要查看該類有沒有實現(xiàn) java.io.Serializable接口。

為什么序列化

1.將結(jié)構(gòu)化的對象變?yōu)闊o結(jié)構(gòu)的字節(jié)流,存儲對象在存儲介質(zhì)中,方便下次使用可以快捷獲取,便于數(shù)據(jù)傳輸。
2.序列化的過程通俗講,就是一個“freeze”的過程,它將一個對象freeze住,然后進行存儲,等到再次需要的時候,再將這個對象de-freeze就可以立即使用。

jdk內(nèi)置序列化

java對序列化提供了很好的支持,當一個對象實現(xiàn)了Serilizable接口,這個對象就可以被序列化,我們不關心其內(nèi)在的原理,只需要了解這個類實現(xiàn)了Serilizable接口,這個類的所有屬性和方法都會自動序列化??梢哉fSerilizable只是一個標識,實際的序列化和反序列化工作是通過java.io.ObjectOuputStream和java.io.ObjectInputStream來完成的。ObjectOutputStream的writeObject方法可以把一個Java對象寫入到流中,ObjectInputStream的readObject方法可以從流中讀取一個Java對象。

transient關鍵字

在實際開發(fā)過程中可能遇到說一個對象中的屬性有些需要序列化有些則不用,比如說一個用戶有些敏感信息(密碼,銀行卡號),為了安全考慮不需要在網(wǎng)絡操作中被傳輸。
這種時候使用transient可以使對應的屬性不被寫入磁盤持久化。換句話說,這個對象的生命周期僅存在調(diào)用者的內(nèi)存中而不被持久化在硬盤中或者網(wǎng)絡傳輸。

//使用例子
package serializable;
import java.io.*;

public class TransientTest implements Serializable{
    static class UserInfo implements Serializable {
        private String name; //此處加static反序列化后仍能取到是因為static修飾的變量存在jvm內(nèi)存中
        private transient String psw;//transient 只能修飾變量(屬性)
        public UserInfo(String name, String psw) {
            this.name = name;
            this.psw = psw;
        }

        public String toString() {
            return "name=" + name + ", psw=" + psw;
        }
    }

    public static void main(String[] args) {
        UserInfo userInfo = new UserInfo("張三", "123456");
        System.out.println(userInfo);
        try {
            // 序列化將對象屬性寫入到UserInfo.txt文件中,被設置為transient的屬性沒有被序列
            ObjectOutputStream o = new ObjectOutputStream(new FileOutputStream("UserInfo.txt"));
            o.writeObject(userInfo);
            o.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            // 重新讀取序列化內(nèi)容
            ObjectInputStream in = new ObjectInputStream(new FileInputStream("UserInfo.txt"));
            UserInfo readUserInfo = (UserInfo) in.readObject();
            System.out.println(readUserInfo.toString()); //修飾transient關鍵字的屬性打印為null
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
測試

Externalizable接口

在java中,對象的序列化可以通過兩種接口實現(xiàn),除了Serilizable接口,還有就是Externalizable接口。
1.若實現(xiàn)Serializable,則所有序列化將會自動執(zhí)行。
2.若實現(xiàn)Externalizable,序列化的過程需手動執(zhí)行,需要在writeExternal方法中進行手工指定所要序列化的變量,與是否被transient修飾無關。

import java.io.*;
/**
 * Created by LJW on 2018/5/28.
 * Externalizable接口測試
 */
public class TestExternalizable implements Externalizable {
    private transient String content = "就算被transient修飾,但如果實現(xiàn)的是Externalizable接口,我還是可能被序列化";
    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
    }

    public static void main(String[] args) throws Exception {
        TestExternalizable et = new TestExternalizable();
        //將TestExternalizable序列化到test.txt文件中
        ObjectOutput out = new ObjectOutputStream(new FileOutputStream(
                new File("test.txt")));
        out.writeObject(et);
        //反序列化test.txt中的信息
        ObjectInput in = new ObjectInputStream(new FileInputStream(new File(
                "test.txt")));
        et = (TestExternalizable) in.readObject();
        System.out.println(et.content);//成功打印content內(nèi)容而不是null,說明反序列化有取到被transient修飾的變量屬性
        out.close();
        in.close();
    }
}

serialVersionUID

在查看jdk源碼的時候,經(jīng)??吹竭@種代碼

private static final long serialVersionUID = 2877471301981509474L; //xxxL 

一個類如果使用了java.io.Serializable接口,在序列化到文件時會自動生成一個serialVersionUID,用于對類進行版本控制(通過判斷實體類的serialVersionUID來驗證版本一致性的。在進行反序列化時,JVM會把傳來的字節(jié)流中的serialVersionUID與本地相應實體類的serialVersionUID進行比較,如果相同就認為是一致的,可以進行反序列化,否則就會出現(xiàn)序列化版本不一致InvalidCalssException的異常)

如何生成

Intellij IDEA可以自動為serializable的類生成一個serialVersionUID。

File->Preferences->Inspections->Serializationissues,將其展開后將serialzable class without "serialVersionUID"打上勾;
之后雙擊下class類名 ALT+ENTER即可生成隨機的serialVersionUID

生成serialVersionUID

總結(jié)

  1. Java序列化就是把對象轉(zhuǎn)換成字節(jié)序列,而Java反序列化就是把字節(jié)序列還原成Java對象,在java中可以通過實現(xiàn)Serializable和Externalizable兩種接口實現(xiàn)序列化。
  2. 采用Java序列化與反序列化技術,一是可以實現(xiàn)數(shù)據(jù)的持久化,在MVC模式中很有用;二是可以對象數(shù)據(jù)的遠程通信,序列化用于通信,服務端把數(shù)據(jù)序列化發(fā)送到客戶端??蛻舳耸盏綌?shù)據(jù)反序列化對數(shù)據(jù)操作。
  3. 序列化的好處:通過序列化可以把數(shù)據(jù)永久保存在硬盤上(通常放在文件里)
  4. transient關鍵字只能修飾屬性,被transient修飾的屬性將不會被序列化(這邊的前提是實現(xiàn)Serializable接口,還有需注意被static修飾的屬性也無法被序列化,static修飾的變量存在jvm內(nèi)存中,如果反序列化后得到static修飾的屬性,是從jvm取而不是反序列化后得到)。
  5. serialVersionUID主要用于反序列化的時候驗證版本的一致性,常在jdk,各種jar包中使用。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容