天天看點

JAVA 序列化、反序列化以及serialVersionUID

 前言

最近接手的老項目也不少,我在看老項目的代碼的時候,順便看到同僚敲代碼,

無聊問到同僚,

這個類為啥要實作序列化?

你看有些類沒序列化不是嘛,但是有些又序列化了,為啥?

為啥你現在建立的也序列化? 

你知道序列化有啥用麼?

一串連問後,得到了短暫的甯靜。

我才發現,

其實很多人都沒有去了解過這些 ,大多數都是腦子裡有個模糊的概念,看到别人這麼做,也跟着這麼做。

是以,我決定寫一篇關于這個序列化、反序列化以及serialVersionUID使用和不使用的簡單介紹文章,希望能幫助一些夥伴把腦子裡模糊的概念給抹掉。

正文

 序列化和反序列化 ,這兩個詞一看就是對着幹的。

簡單了解:

序列化,就是将一個東西 給變化成序列。

反序列化,就是将序列給變化成一個東西。

這裡順便一提,serialVersionUID 其實就是這個東西的一個号碼,就行是咱們的身份證一樣。

文筆拙劣,我們結合代碼來看:

首先我們建立一個類,用于咱們接下來的序列化操作使用,Cat.class:

ps: 該篇文章主介紹實作Serializable 接口 來達到序列化。

import java.io.Serializable;

/**
 * @Author : JCccc
 * @CreateTime : 2020/4/21
 * @Description :
 **/
public class Dog implements Serializable {
    
    
    private  String name;
    private  Integer age;

    @Override
    public String toString() {
        return "Dog{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}      

可以看到上面的類 Dog實作了Serializable, 标記這個類是可以序列化的。

有人也注意到了,為什麼沒有弄serialVersionUID ? 

其實咱們如果不手動設定serialVersionUID,會有預設計算出的serialVersionUID的。後面再讨論為什麼有手動弄serialVersionUID的場景。

結合代碼

序列化

import com.jc.mytest.model.Dog;
import java.io.*;

/**
 * @Author : JCccc
 * @CreateTime : 2020/4/21
 * @Description :
 **/
public class SerializeTest {


    public static void main(String[] args) throws IOException, ClassNotFoundException {

        //序列化對象-IO流-存儲
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:\\dogInfoText.out"));

        Dog dog=new Dog();
        dog.setName("阿福");
        dog.setAge(1);
        objectOutputStream.writeObject(dog);

        objectOutputStream.flush();
        objectOutputStream.close();

    }


}      

運作一下,可以看到我們的D盤生成了這個Dog類型序列化後的檔案,

JAVA 序列化、反序列化以及serialVersionUID

裡面全是 ‘亂碼’,沒事亂碼我們看不懂,但是jvm能看懂。

JAVA 序列化、反序列化以及serialVersionUID

反序列化

public class SerializeTest {


    public static void main(String[] args) throws IOException, ClassNotFoundException {
        
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\dogInfoText.out"));
        Dog dog = (Dog) objectInputStream.readObject();


        System.out.println("dog's name:"+dog.getName());
    }


}      

運作結果,跟我們序列化進去的Dog設定的字段屬性值一樣:

JAVA 序列化、反序列化以及serialVersionUID

序列化和反序列化的簡單使用操作已經完畢,作用顯然都知道了,簡單的了解就是轉存為位元組流可以友善傳輸,然後反序列化可以快速地拿到原來的對象。

那麼接下來我們來看看為什麼要加 serialVersionUID ? 如:

private static final long serialVersionUID = -8567374045705746827L;      
private static final long serialVersionUID = 1L;      

上面有說過如果我們不手動加這個 serialVersionUID,是會預設生成一個的,隻是我們看不到。

上面也有說過,這個serialVersionUID就像是這個類的身份證号碼一樣,具有唯一識别的性質。

舉例:

原本我們的Dog類隻有2個字段屬性,

JAVA 序列化、反序列化以及serialVersionUID

然後我們進行了序列化, 這時候,對應預設對應的 serialVersionUID,綁定的内容是這個Dog,有2個字段屬性,

已經序列化儲存到D:\\dogInfoText.out 檔案裡面了。

這時候我們進行一個修改,增加一個字段,nickName 。如:

JAVA 序列化、反序列化以及serialVersionUID

此時,我們進行反序列化的操作,就會報錯:

JAVA 序列化、反序列化以及serialVersionUID

因為系統預設給之前序列化的Dog是生成了一個serialVersionUID的,裡面綁定的屬性隻有兩個,現在你讓三個屬性的去接收反序列化後的Dog類,且身份證号碼不一樣,肯定報錯了。

這時候避免這種情況的出現,手動設定一個serialVersionUID就可以,如:

JAVA 序列化、反序列化以及serialVersionUID

加上serialVersionUID之後的Dog,序列化之後,無論後面怎麼修改,隻要serialVersionUID不變,反序列化就可以正常進行。

(如果沒有設定idea自動生成,手動設定1L也是經常使用的手段)

最後再補充兩點, 哪些字段是不能被序列化的呢?

1. static 修飾的, 因為序列化操作是對于 堆 區 ,而static的在全局區

2.transient 修飾的字段 ,在使用implements Serializable 的時候,也是避開序列化的

ps: 至于子類和父類這些繼承關系,序列化的時候應該遵守什麼規則,這些就留個大家去額外擴充下吧。

好,該篇文章就到此結束。