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