java序列化和反序列化

對象序列化(serialization)反序列化(deserialization)是將對象轉(zhuǎn)化為便于傳輸?shù)母袷竭M(jìn)行發(fā)送和接收的兩個操作厢破。常見的序列化格式有字節(jié)數(shù)組,json字符串笆焰,xml字符串等。
本次討論的是java中的對象字節(jié)序列化嚷掠。

哪些東西可以是字節(jié)荞驴?圖片可以是字節(jié),文件可以是字節(jié)粟焊,一個字符串也可以是字節(jié)孙蒙,嗯悲雳,宇宙間的一切事物都可以用字節(jié)表示。當(dāng)然合瓢,對象也可以是字節(jié)。java的序列化就是將對象轉(zhuǎn)化為字節(jié)流顿苇,以便在進(jìn)程或網(wǎng)絡(luò)之間進(jìn)行傳輸税弃,而在接收方,需要以相同的方式對字節(jié)流進(jìn)行反序列化幔翰,得到傳輸?shù)膶ο蟆?/p>

jdk提供了序列化和反序列化相關(guān)實(shí)現(xiàn)。

1.序列化

使用此方法進(jìn)行序列化的對象必須實(shí)現(xiàn)Serializable接口遗增,不然在進(jìn)行序列化時(shí)會拋出NotSerializableException異常。

    public static byte[] toBytes(Serializable obj) throws IOException {
        try(ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        ){
            objectOutputStream.writeObject(obj);
            return byteArrayOutputStream.toByteArray();
        }
    }

ObjectOutputStream的writeObject方法將對象序列化后寫入一個字節(jié)流中霍狰,而這個字節(jié)流就是在初始化ObjectOutputStream對象時(shí)傳入的字節(jié)流饰及,這里使用ByteArrayOutputStream,可以獲取到字節(jié)流中的字節(jié)數(shù)組步悠。

2.反序列化

對應(yīng)序列化,反序列化應(yīng)該是將字節(jié)數(shù)組轉(zhuǎn)化為對象鼎兽。

    public static Serializable toObj(byte[] bytes) throws IOException, ClassNotFoundException {
        try(ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
            ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        ){ 
            Object o = objectInputStream.readObject();
            return (Serializable) o;
        }
    }

ByteArrayInputStream將字節(jié)數(shù)組轉(zhuǎn)化為字節(jié)流谚咬。而ObjectInputStream的readObject方法將字節(jié)流轉(zhuǎn)化為對象尚粘。

3.測試

建立一個Student類,并實(shí)現(xiàn)Serializable接口郎嫁。

public class Student implements Serializable{
    private int id;
    private String code;
    private String name;
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", code='" + code + '\'' +
                ", name='" + name + '\'' +
                '}';
    }
    //省略set/get方法
···

測試代碼:

        //創(chuàng)建student對象
        Student student = new Student(1,"2014213880","劉瑞杰");
        //序列化
        byte[] bytes = toBytes(student);
        System.out.println(bytes.length);
        //反序列化
        Student student0 = (Student) toObj(bytes);
        System.out.println(student0);

輸出結(jié)果:

111
Student{id=1, code='2014213880', name='劉瑞杰'}

4.關(guān)于serialVersionUID

我們可能經(jīng)常會看到泽铛,在一些類中實(shí)現(xiàn)了Serializable接口,同時(shí)還定義了一個private final static long型的變量杠茬,名字叫serialVersionUID。

        private final static long serialVersionUID = 123456789L

那么這個變量有什么作用瓢喉?

上面的例子都是在對象序列化之后舀透,立即將結(jié)果反序列化,這樣肯定是沒任何問題的逗载。但是現(xiàn)在設(shè)想一個場景,如果將對象序列化后厉斟,并沒有馬上反序列化,如將對象保存到文件中擦秽,然后修改類的定義,比如在Student類中加一個字段缩搅。
然后再進(jìn)行反序列化触幼,此時(shí)會發(fā)生什么樣的事情。

首先編寫兩個方法置谦,分別是將對象保存到文件中,以及從文件中讀取之前保存的對象瘟栖。

寫文件:

    public static void toFile(Serializable obj, String filePath) throws IOException {
        try(FileOutputStream fileOutputStream = new FileOutputStream(filePath);
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
        ){
            objectOutputStream.writeObject(obj);
        }
    }

讀文件:

    public static Serializable fromFile(String filePath) throws IOException, ClassNotFoundException {
        try(FileInputStream fileInputStream = new FileInputStream(filePath);
            ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
        ) {
            return (Serializable) objectInputStream.readObject();
        }
    }

測試代碼:
首先保存Student對象:

        String path = Test.class.getClassLoader().getResource("").getPath();
        toFile(student, path+"student.obj");

執(zhí)行完成后半哟,
修改Student類的定義:

public class Student implements Serializable{
    private int id;
    private String code;
    private String name;
    private String clazz;//加個字段
    //省略set/get方法
···

讀取文件中的Student對象:

        Student student1 = (Student) fromFile(path+"student.obj");
        System.out.println(student1);

執(zhí)行這段代碼签餐,意料之中,拋出了異常:

Exception in thread "main" java.io.InvalidClassException: serilize.Student;
 local class incompatible: 
stream classdesc serialVersionUID = 6643383736516292602, 
local class serialVersionUID = -1315177638240754633

大概可以看出來缅茉,說是加載的類不匹配,當(dāng)然了,文件中的是舊的Student類译打,而程序中的是新的Student類,當(dāng)然會不匹配乔询。

不過韵洋,有時(shí)候黄锤,我們不希望每次修改了類的定義后食拜,以前保存的實(shí)例就不能夠使用了,這樣會導(dǎo)致每次更新程序會丟失很多數(shù)據(jù)负甸。
有些時(shí)候,修改后的類是完全可以兼容舊的類的打月。比如這里Student類只是加了一個clazz班級字段蚕捉,之前的所有字段都沒有變化。
而程序是通過比較新舊兩個類的serialVersionUID來判斷是否是不一樣的兩個類迫淹。所以我們只需要覆蓋serialVersionUID就可以做到兼容了。

覆蓋serialVersionUID需要滿足得條件:
1.static 靜態(tài)變量 2.final 常量 3.long 長整型 4.名稱為serialVersionUID

在Student類中加入這個變量:

public class Student implements Serializable{
    private final static long serialVersionUID = 1L;
···

再次重復(fù)上面的操作:寫文件->類中加字段->讀文件
這次沒拋異常充易,成功輸出了正確的結(jié)果荸型。

Student{id=1, code='2014213880', name='劉瑞杰'}

當(dāng)然,并不是說稿静,覆蓋了serialVersionUID就可以做到完全兼容,數(shù)據(jù)完全不會丟失改备。如果修改已有的變量的類型蔓倍,如將Student類的id屬性改為字符串型,不用試都知道偶翅,反序列化時(shí)一定會拋異常。

InvalidClassException: serilize.Student; incompatible types for field id
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末母剥,一起剝皮案震驚了整個濱河市环疼,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌炫隶,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件爸吮,死亡現(xiàn)場離奇詭異望门,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)桐早,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門厨剪,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人祷膳,你說我怎么就攤上這事〔缶” “怎么了勇皇?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長门烂。 經(jīng)常有香客問我,道長屯远,這世上最難降的妖魔是什么拖叙? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘崩溪。我一直安慰自己,他們只是感情好伶唯,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布惧盹。 她就那樣靜靜地躺著,像睡著了一般粹断。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瓶埋,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天诊沪,我揣著相機(jī)與錄音,去河邊找鬼晕粪。 笑死,一個胖子當(dāng)著我的面吹牛巫湘,可吹牛的內(nèi)容都是我干的橄仆。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼怠褐,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了奈懒?” 一聲冷哼從身側(cè)響起宪巨,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎极祸,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體遥金,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡稿械,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了页眯。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡窝撵,死狀恐怖述吸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情蝌矛,我是刑警寧澤,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布隆豹,位于F島的核電站茅逮,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏献雅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一侯谁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧墙贱,春花似錦、人聲如沸惨撇。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽纺棺。三九已至邪狞,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間帆卓,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工剑令, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人棚蓄。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓碍脏,卻偏偏與公主長得像,于是被迫代替她去往敵國和親典尾。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容

  • JAVA序列化機(jī)制的深入研究 對象序列化的最主要的用處就是在傳遞,和保存對象(object)的時(shí)候,保證對象的完整...
    時(shí)待吾閱讀 10,863評論 0 24
  • 1.背景 某天河闰,我在寫代碼定義 bean 的時(shí)候褥紫,順手寫了個 public class User implemen...
    李眼鏡閱讀 779評論 0 2
  • 什么是序列化? 序列化是將對象存儲為二進(jìn)制格式部念。在序列化的過程中,對象和它的元數(shù)據(jù)(比如對象的類名和它的屬性名稱)...
    Chokez閱讀 1,105評論 0 0
  • 1. Java序列化和反序列化(What) Java序列化(Serialize)是指將一個Java對象寫入IO流中...
    悠揚(yáng)前奏閱讀 882評論 2 1
  • 一、 序列化和反序列化概念 Serialization(序列化)是一種將對象以一連串的字節(jié)描述的過程门驾;反序列化de...
    步積閱讀 1,443評論 0 10