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類型序列化後的文件,

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

 

反序列化

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設置的字段屬性值一樣:

 

序列化和反序列化的簡單使用操作已經完畢,作用顯然都知道了,簡單的理解就是轉存爲字節流可以方便傳輸,然後反序列化可以快速地拿到原來的對象。

 

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

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

 

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

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

 

舉例:

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

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

已經序列化保存到D:\\dogInfoText.out 文件裏面了。

 

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

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

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

 

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

 

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

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

 

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

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

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

 

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

 

 

 

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

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章