Java序列化之serialVersionUID的用法解讀
Java序列化之serialVersionUID
今天講一講Java對象中的serialVersionUID,先從序列化講起。
什么是序列化
序列化,簡單的說,就是將一個對象轉(zhuǎn)化(編碼)成可以傳輸?shù)妮敵隽鳎ㄗ止?jié)流)。而反序列化就是序列化的逆過程,將輸入流轉(zhuǎn)化(構(gòu)建)成一個對象。
為什么要序列化
字節(jié)流可以用于網(wǎng)絡(luò)傳輸和存儲在磁盤,而對象需要轉(zhuǎn)化成字節(jié)流才能在網(wǎng)絡(luò)中傳輸和在磁盤上存儲。
網(wǎng)絡(luò)傳輸就好比打電話,聲音是無法直接從電話的一端傳到另一端,因此需要將聲音轉(zhuǎn)成電信號進行傳播。
另一方面,Java對象是保存在JVM的堆內(nèi)存中的,也就是說,如果JVM堆不存在了,那么對象也就跟著消失了,而序列化提供了可以把對象保存下來的方案。
serialVersionUID是個啥
說到序列化,serialVersionUID是個不得不談的話題。
serialVersionUID 是 Java 為每個序列化類(實現(xiàn)java.io.Serializable接口的類)產(chǎn)生的版本標識, 可用來保證在反序列時,發(fā)送方發(fā)送的和接受方接收的是可兼容的對象。
如果接收方接收的類的 serialVersionUID 與發(fā)送方發(fā)送的 serialVersionUID 不一致,進行反序列時會拋出 InvalidClassException。
怎么生成serialVersionUID
下載GenerateSerialVersionUID插件,就可以自動生產(chǎn)這個序列類的serialVersionUID了。

serialVersionUID是一成不變的嗎
達咩!
serialVersionUID 是 Java 為每個序列化類產(chǎn)生的版本標識??!
Java序列化機制會根據(jù)編譯的Class自動生成一個serialVersionUID作序列化版本比較用。
如果Class文件的類名、方法名稱發(fā)生改變,serialVersionUID就會改變。
如果Class文件沒有發(fā)生變化(增加空格,換行,增加注釋等等),就算再編譯多次,serialVersionUID也不會變化的。
import java.io.Serializable;
public class Person implements Serializable {
// 原本的serialVersionUID
private static final long serialVersionUID = 904XXXXXXXXXX662L;
private int age;
private String name;
private String address;
public Person(int age, String name, String address) {
this.age = age;
this.name = name;
this.address = address;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
import java.io.Serializable;
public class Person implements Serializable {
// 加上toSting函數(shù)的serialVersionUID
private static final long serialVersionUID = 841XXXXXXXXXXXXX884L;
private int age;
private String name;
private String address;
public Person(int age, String name, String address) {
this.age = age;
this.name = name;
this.address = address;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Person{" +
"age=" + age +
", name='" + name + '\'' +
", address='" + address + '\'' +
'}';
}
}
如果我手動改了serialVersionUID=11111111L會怎樣?如果接收方接收的類的 serialVersionUID 與發(fā)送方發(fā)送的 serialVersionUID 不一致,進行反序列時會拋出 InvalidClassException。
Exception in thread "main" java.io.InvalidClassException: SerializableStudy.Person; local class incompatible: stream classdesc serialVersionUID = 841XXXXXXXXXXXXX884, local class serialVersionUID = 11111111
序列化和反序列化
序列化要把對象寫入輸出流中,反序列化就是將輸出流重新構(gòu)建對象,二者為逆過程。當serialVersionUID改變時,一定要重新序列化,再進行反序列化。
話不多說,放代碼。
以下是基于上面序列化類Person,做序列化和反序列化的演示:
public class SerialTest {
public static void main(String[] args) throws IOException {
// 序列化
Person p = new Person(0,"aaa","bbbbb");
// 指定文件生成輸出流
FileOutputStream fos = new FileOutputStream("person.txt");
// 將對象寫出到指定的輸出流
ObjectOutputStream oos = new ObjectOutputStream(fos);
// 將指定的對象寫入ObjectOutputStream。
oos.writeObject(p);
// 刷新流
oos.flush();
oos.close();
}
}
public class DeserialTest {
public static void main(String[] args) throws IOException, ClassNotFoundException {
// 反序列化
// 根據(jù)指定文件生產(chǎn)輸入流
FileInputStream fis = new FileInputStream("person.txt");
// 從指定的輸入流中讀回對象消息
ObjectInputStream ois = new ObjectInputStream(fis);
// 從ObjectInputStream讀取一個對象
Person p = (Person) ois.readObject();
ois.close();
System.out.println(p.toString());
}
}
輸出的結(jié)果為:

注意:
將指定對象寫入ObjectOutputStream時,存儲針對對象本身而不是針對類,沒有實現(xiàn)序列化的類不會參與序列化和反序列化??!
舉個例子,如果Person中設(shè)置一個沒有實現(xiàn)序列化的父類Home:
public class Home {
private String home;
public String getHome() {
return home;
}
public void setHome(String home) {
this.home = home;
}
}
public class Person extends Home implements Serializable {
......
}
在序列化和反序列化的過程中,即使定義了Person對象的home屬性,由于Home中沒有實現(xiàn)序列類,因此對象的home屬性不會進行序列化處理。

總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot如何切換成其它的嵌入式Servlet容器(Jetty和Undertow)
這篇文章主要介紹了SpringBoot如何切換成其它的嵌入式Servlet容器(Jetty和Undertow),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-07-07
RocketMQ?NameServer架構(gòu)設(shè)計啟動流程
這篇文章主要為大家介紹了RocketMQ?NameServer架構(gòu)設(shè)計啟動流程,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-02-02
Spring Security 在 Spring Boot 中的使用詳解【集中式】
這篇文章主要介紹了Spring Security 在 Spring Boot 中的使用【集中式】,本文通過圖文并茂的形式給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-10-10
SpringBoot中@Value獲取值和@ConfigurationProperties獲取值用法及比較
在Spring Boot中,@Value注解是一個非常有用的特性,它允許我們將外部的配置注入到我們的Bean中,@ConfigurationProperties用于將配置文件中的屬性綁定到 Java Bean 上,本文介紹了@Value獲取值和@ConfigurationProperties獲取值用法及比較,需要的朋友可以參考下2024-08-08
Spring MVC傳遞接收參數(shù)方式小結(jié)
大家在開發(fā)中經(jīng)常會用到Spring MVC Controller來接收請求參數(shù),主要常用的接收方式就是通過實體對象以及形參等方式、有些用于GET請求,有些用于POST請求,有些用于兩者,下面介紹幾種常見的Spring MVC傳遞接收參數(shù)的方式2021-11-11

