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

serialVersionUID是一成不變的嗎
達(dá)咩!
serialVersionUID 是 Java 為每個(gè)序列化類產(chǎn)生的版本標(biāo)識(shí)!!
Java序列化機(jī)制會(huì)根據(jù)編譯的Class自動(dòng)生成一個(gè)serialVersionUID作序列化版本比較用。
如果Class文件的類名、方法名稱發(fā)生改變,serialVersionUID就會(huì)改變。
如果Class文件沒有發(fā)生變化(增加空格,換行,增加注釋等等),就算再編譯多次,serialVersionUID也不會(huì)變化的。
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 + '\'' +
'}';
}
}
如果我手動(dòng)改了serialVersionUID=11111111L會(huì)怎樣?如果接收方接收的類的 serialVersionUID 與發(fā)送方發(fā)送的 serialVersionUID 不一致,進(jìn)行反序列時(shí)會(huì)拋出 InvalidClassException。
Exception in thread "main" java.io.InvalidClassException: SerializableStudy.Person; local class incompatible: stream classdesc serialVersionUID = 841XXXXXXXXXXXXX884, local class serialVersionUID = 11111111
序列化和反序列化
序列化要把對(duì)象寫入輸出流中,反序列化就是將輸出流重新構(gòu)建對(duì)象,二者為逆過(guò)程。當(dāng)serialVersionUID改變時(shí),一定要重新序列化,再進(jìn)行反序列化。
話不多說(shuō),放代碼。
以下是基于上面序列化類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");
// 將對(duì)象寫出到指定的輸出流
ObjectOutputStream oos = new ObjectOutputStream(fos);
// 將指定的對(duì)象寫入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");
// 從指定的輸入流中讀回對(duì)象消息
ObjectInputStream ois = new ObjectInputStream(fis);
// 從ObjectInputStream讀取一個(gè)對(duì)象
Person p = (Person) ois.readObject();
ois.close();
System.out.println(p.toString());
}
}
輸出的結(jié)果為:

注意:
將指定對(duì)象寫入ObjectOutputStream時(shí),存儲(chǔ)針對(duì)對(duì)象本身而不是針對(duì)類,沒有實(shí)現(xiàn)序列化的類不會(huì)參與序列化和反序列化??!
舉個(gè)例子,如果Person中設(shè)置一個(gè)沒有實(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 {
......
}
在序列化和反序列化的過(guò)程中,即使定義了Person對(duì)象的home屬性,由于Home中沒有實(shí)現(xiàn)序列類,因此對(duì)象的home屬性不會(huì)進(jìn)行序列化處理。

總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
mac下idea的svn密碼記不住的問(wèn)題及處理方法
這篇文章主要介紹了mac下idea的svn密碼記不住的問(wèn)題及處理方法,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-09-09
java實(shí)現(xiàn)web實(shí)時(shí)消息推送的七種方案
這篇文章主要為大家介紹了java實(shí)現(xiàn)web實(shí)時(shí)消息推送的七種方案示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07
springboot線程池監(jiān)控的簡(jiǎn)單實(shí)現(xiàn)
本文主要介紹了springboot線程池監(jiān)控的簡(jiǎn)單實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01
使用Mybatis-plus實(shí)現(xiàn)時(shí)間自動(dòng)填充(代碼直接可用)
這篇文章主要介紹了使用Mybatis-plus實(shí)現(xiàn)時(shí)間自動(dòng)填充(代碼直接可用),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
Java語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單FTP軟件 FTP軟件效果圖預(yù)覽之下載功能(2)
這篇文章主要為大家詳細(xì)介紹了Java語(yǔ)言實(shí)現(xiàn)簡(jiǎn)單FTP軟件,F(xiàn)TP軟件效果圖預(yù)覽之下載功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-03-03
java面向?qū)ο笤O(shè)計(jì)原則之開閉原則示例解析
這篇文章主要介紹了java面向?qū)ο笤O(shè)計(jì)原則之開閉原則的示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2021-10-10
Java?spring?通過(guò)注解方式創(chuàng)建對(duì)象的示例詳解
這篇文章主要介紹了java?spring?通過(guò)注解方式創(chuàng)建對(duì)象,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-02-02
SpringBoot項(xiàng)目使用validated實(shí)現(xiàn)參數(shù)校驗(yàn)框架
當(dāng)談到Spring的參數(shù)校驗(yàn)功能時(shí),@Validated注解無(wú)疑是一個(gè)重要的利器,它為我們提供了一種簡(jiǎn)單而又強(qiáng)大的方式來(lái)驗(yàn)證請(qǐng)求參數(shù)的合法性,保證了系統(tǒng)的穩(wěn)定性和安全性,本文將介紹Spring Validated的基本用法以及在實(shí)際項(xiàng)目中的應(yīng)用,需要的朋友可以參考下2024-05-05

