前言
最近接手的老項目也不少,我在看老項目的代碼的時候,順便看到同僚敲代碼,
無聊問到同僚,
這個類為啥要實作序列化?
你看有些類沒序列化不是嘛,但是有些又序列化了,為啥?
為啥你現在建立的也序列化?
你知道序列化有啥用麼?
一串連問後,得到了短暫的甯靜。
我才發現,
其實很多人都沒有去了解過這些 ,大多數都是腦子裡有個模糊的概念,看到别人這麼做,也跟着這麼做。
是以,我決定寫一篇關于這個序列化、反序列化以及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: 至于子類和父類這些繼承關系,序列化的時候應該遵守什麼規則,這些就留個大家去額外擴充下吧。
好,該篇文章就到此結束。