Java中序列化與反序列化的定義及代碼示例
序列化與反序列化的定義
序列化(Serialization)與反序列化(Deserialization)是編程中常見(jiàn)的兩個(gè)概念,它們主要涉及到將數(shù)據(jù)結(jié)構(gòu)或?qū)ο鬆顟B(tài)轉(zhuǎn)換為可以存儲(chǔ)或傳輸?shù)母袷?,以及將存?chǔ)或傳輸?shù)母袷睫D(zhuǎn)換回原始的數(shù)據(jù)結(jié)構(gòu)或?qū)ο鬆顟B(tài)的過(guò)程。
這兩個(gè)過(guò)程在數(shù)據(jù)持久化、網(wǎng)絡(luò)通信、對(duì)象深拷貝等多個(gè)場(chǎng)景中發(fā)揮著重要作用。
序列化(Serialization)
序列化是指將數(shù)據(jù)結(jié)構(gòu)或?qū)ο鬆顟B(tài)轉(zhuǎn)換成可以存儲(chǔ)或傳輸?shù)母袷降倪^(guò)程。這個(gè)格式通常是平臺(tái)無(wú)關(guān)的,比如JSON、XML、二進(jìn)制格式等,以便可以在不同的系統(tǒng)或環(huán)境中進(jìn)行交換。序列化后的數(shù)據(jù)可以存儲(chǔ)在文件中,或者通過(guò)網(wǎng)絡(luò)發(fā)送給其他系統(tǒng)。
為什么需要序列化?
- 數(shù)據(jù)持久化:將對(duì)象狀態(tài)保存到文件中,以便在程序關(guān)閉或系統(tǒng)重啟后能夠重新加載這些數(shù)據(jù)。
- 網(wǎng)絡(luò)傳輸:在網(wǎng)絡(luò)通信中,數(shù)據(jù)需要在客戶(hù)端和服務(wù)器之間傳輸,序列化可以將對(duì)象轉(zhuǎn)換成字節(jié)流,便于在網(wǎng)絡(luò)上傳輸。
- 對(duì)象深拷貝:通過(guò)序列化一個(gè)對(duì)象,然后立即進(jìn)行反序列化,可以實(shí)現(xiàn)對(duì)象的深拷貝。
反序列化(Deserialization)
反序列化是序列化的逆過(guò)程,即將序列化后的數(shù)據(jù)(如JSON、XML、二進(jìn)制格式等)轉(zhuǎn)換回原始的數(shù)據(jù)結(jié)構(gòu)或?qū)ο鬆顟B(tài)。這個(gè)過(guò)程允許程序在需要時(shí)重新構(gòu)建對(duì)象,并使用其原始數(shù)據(jù)。
為什么需要反序列化?
- 數(shù)據(jù)恢復(fù):在程序啟動(dòng)時(shí),從文件中讀取序列化后的數(shù)據(jù),反序列化成對(duì)象,以恢復(fù)程序運(yùn)行前的狀態(tài)。
- 數(shù)據(jù)接收:在網(wǎng)絡(luò)通信中,接收到的序列化數(shù)據(jù)需要被反序列化成對(duì)象,以便在本地系統(tǒng)中使用。
Java的序列化
Java序列化是指將Java對(duì)象的狀態(tài)轉(zhuǎn)換為可以保存或傳輸?shù)母袷降倪^(guò)程。Java提供了多種序列化方式,以滿足不同的需求和場(chǎng)景。以下是Java序列化的幾種主要方式:
1. 實(shí)現(xiàn)Serializable接口
- 特點(diǎn):這是Java默認(rèn)的序列化方式,通過(guò)實(shí)現(xiàn)java.io.Serializable接口來(lái)標(biāo)記一個(gè)類(lèi)可以被序列化。這種序列化方式是隱式的,會(huì)自動(dòng)序列化所有非static和transient關(guān)鍵字修飾的成員變量。
- 使用場(chǎng)景:適用于大多數(shù)需要序列化的場(chǎng)景,特別是當(dāng)對(duì)象的狀態(tài)需要被持久化或在網(wǎng)絡(luò)間傳輸時(shí)。
2. 實(shí)現(xiàn)Externalizable接口
- 特點(diǎn):與Serializable接口類(lèi)似,但Externalizable接口提供了更高的靈活性。實(shí)現(xiàn)該接口的類(lèi)必須手動(dòng)實(shí)現(xiàn)writeExternal()和readExternal()方法,以控制序列化和反序列化的過(guò)程。
- 使用場(chǎng)景:適用于需要精確控制序列化過(guò)程的場(chǎng)景,比如只序列化對(duì)象的某些部分,或者需要對(duì)序列化數(shù)據(jù)進(jìn)行加密等。
3. 使用JSON序列化庫(kù)
- 常用庫(kù):如Jackson、Gson等。
- 特點(diǎn):將對(duì)象轉(zhuǎn)換為JSON格式的字符串進(jìn)行序列化,可以跨語(yǔ)言、跨平臺(tái)進(jìn)行數(shù)據(jù)交換。JSON格式易于閱讀和編寫(xiě),也易于機(jī)器解析和生成。
- 使用場(chǎng)景:適用于需要與其他語(yǔ)言或系統(tǒng)交互的場(chǎng)景,或者當(dāng)對(duì)象需要被存儲(chǔ)為文本格式時(shí)。
4. 使用XML序列化庫(kù)
- 常用庫(kù):如JAXB、XStream等。
- 特點(diǎn):將對(duì)象轉(zhuǎn)換為XML格式的字符串進(jìn)行序列化,同樣可以跨語(yǔ)言、跨平臺(tái)進(jìn)行數(shù)據(jù)交換。XML格式具有良好的可擴(kuò)展性和可讀性。
- 使用場(chǎng)景:適用于需要遵循特定XML標(biāo)準(zhǔn)的場(chǎng)景,或者當(dāng)對(duì)象需要被存儲(chǔ)為易于人類(lèi)閱讀的格式時(shí)。
5. 使用二進(jìn)制序列化庫(kù)
- 常用庫(kù):如Protocol Buffers(protobuf)、Apache Thrift、Apache Avro等。
- 特點(diǎn):將對(duì)象序列化為二進(jìn)制格式進(jìn)行傳輸和存儲(chǔ),效率較高。這些庫(kù)通常提供了豐富的數(shù)據(jù)結(jié)構(gòu)和高效的編碼解碼算法。
- 使用場(chǎng)景:適用于對(duì)性能要求較高的場(chǎng)景,比如需要頻繁序列化和反序列化大量數(shù)據(jù)的系統(tǒng)。
6. 其他序列化方式
- Hessian序列化:Hessian是一種通過(guò)網(wǎng)絡(luò)傳輸和存儲(chǔ)Java對(duì)象的二進(jìn)制格式,可以跨語(yǔ)言、跨平臺(tái)使用,效率較高。
- Kryo序列化:Kryo是一種高性能的Java序列化庫(kù),序列化和反序列化速度都很快,但只能用于Java環(huán)境。
Java提供了多種序列化方式,每種方式都有其特點(diǎn)和適用場(chǎng)景。在實(shí)際應(yīng)用中,可以根據(jù)具體需求和場(chǎng)景選擇合適的方式進(jìn)行Java對(duì)象的序列化。需要注意的是,除了Java默認(rèn)的序列化方式外,其他方式通常需要引入相應(yīng)的第三方庫(kù)。
Java默認(rèn)序列化與反序列化示例
我們先將一些Java的變量和一個(gè)Car對(duì)象序列化到 d盤(pán)的一個(gè)名為 data.dat 的文件中。
/**
* ObjectOutputStream 的使用,完成數(shù)據(jù)的序列化
* ObjectOutputStream 是序列化
*/
public class ObjectOutputStreamDemo {
public static void main(String[] args) throws IOException {
//序列化后,保存到文本格式不是純文本txt,而是按照它的格式來(lái)保存
String filePath = "d:\\data.dat";
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(filePath));
oos.writeInt(100);//int -> Integer(實(shí)現(xiàn)了Serializable)
oos.writeBoolean(true);//boolean -> Boolean(實(shí)現(xiàn)了 Serializable)
oos.writeChar('o');//char -> Character(實(shí)現(xiàn)了Serializable)
oos.writeDouble(4.4);//double -> Double(實(shí)現(xiàn)了 Serializable)
oos.writeUTF("對(duì)象輸出流");//String (實(shí)現(xiàn)了Serializable)
oos.writeObject(new Car("xiaomi su7","Pro","Gulf Blue","300,000"));
oos.close();
System.out.println("數(shù)據(jù)以序列化的形式保存完成");
/*
如果Car沒(méi)有實(shí)現(xiàn) Serializable接口,會(huì)報(bào)錯(cuò)如下:
Exception in thread "main" java.io.NotSerializableException: org.example.io.demo.lession20.Car
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at org.example.io.demo.lession20.ObjectOutputStreamDemo.main(ObjectOutputStreamDemo.java:24)
*/
}
}
@Data
class Car implements Serializable {
private String name;
private String type;
private static String color ;
private transient String price ;
public Car(String name,String type,String color,String price){
this.name = name;
this.type = type;
this.color = color;
this.price = price;
}
}
執(zhí)行main方法后,會(huì)在d盤(pán)根目錄下看到一個(gè) data.dat的文件。

使用記事本打開(kāi),依稀可以看到點(diǎn)什么,但明顯不是txt格式的文本。

下面我們將這個(gè)文件反序列化至程序中并打印出來(lái):
/**
* 讀取序列化文件,并通過(guò)反序列化恢復(fù)數(shù)據(jù)
* ObjectInputStream 是反序列化
*/
public class ObjectInputStreamDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
//指定反序列化的文件
String filePath = "d:\\data.dat";
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(filePath));
//讀?。捶葱蛄谢┑捻樞蛐枰捅4鏀?shù)據(jù)(即序列化)的順序一致,否則會(huì)有異常
System.out.println(ois.readInt());
System.out.println(ois.readBoolean());
System.out.println(ois.readChar());
System.out.println(ois.readDouble());
System.out.println(ois.readUTF());
System.out.println(ois.readObject().toString());
ois.close();
}
}
輸出結(jié)果為:
100
true
o
4.4
對(duì)象輸出流
Car(name=xiaomi su7, type=Pro, price=null)
Java 序列化與反序列化注意事項(xiàng):
- 讀寫(xiě)順序要一致。
- 序列化與反序列化對(duì)象要實(shí)現(xiàn) Serializable 接口。
- 序列化中的類(lèi)中,建議添加SerialVersionUID,為了提高版本的兼容性。
- 序列化對(duì)象時(shí),默認(rèn)將里面所有的屬性都進(jìn)行序列化,除了stati、transient 修飾的成員。
- 序列化對(duì)象時(shí),要求里面屬性的類(lèi)型也需要實(shí)現(xiàn)序列化接口。
- 序列化具備可繼承性,也就是如果某類(lèi)已經(jīng)實(shí)現(xiàn)了序列化,則它的所有子類(lèi)也默認(rèn)實(shí)現(xiàn)了序列化。
TIPS:static 和 transient 在序列化中的區(qū)別
static關(guān)鍵字
- 定義:static關(guān)鍵字用于修飾類(lèi)的成員變量和方法,使其成為類(lèi)變量或類(lèi)方法,而不是實(shí)例變量或?qū)嵗椒ān?lèi)變量屬于類(lèi)本身,而非類(lèi)的任何特定實(shí)例。
- 序列化行為:在序列化過(guò)程中,被static修飾的變量(即類(lèi)變量)不會(huì)被序列化。這是因?yàn)閟tatic變量是類(lèi)級(jí)別的,它們不屬于任何特定的對(duì)象實(shí)例,而是由所有實(shí)例共享。因此,序列化一個(gè)對(duì)象時(shí),不會(huì)包含其類(lèi)變量的值,因?yàn)轭?lèi)變量的值在JVM中是全局的,與特定的序列化實(shí)例無(wú)關(guān)。
- 反序列化影響:反序列化一個(gè)對(duì)象時(shí),被static修飾的變量會(huì)保持其在JVM中的當(dāng)前值,而不是從序列化數(shù)據(jù)中恢復(fù)。這是因?yàn)殪o態(tài)變量是全局的,不受單個(gè)對(duì)象實(shí)例的序列化/反序列化過(guò)程影響。
transient關(guān)鍵字
- 定義:transient關(guān)鍵字用于修飾類(lèi)的成員變量,以指示該變量在序列化時(shí)不應(yīng)被序列化。
- 序列化行為:當(dāng)一個(gè)對(duì)象被序列化時(shí),所有非transient修飾的成員變量都會(huì)被序列化,而transient修飾的變量則會(huì)被忽略。這意味著,在序列化后的數(shù)據(jù)中,不會(huì)包含任何被transient修飾的變量的值。
- 反序列化影響:在反序列化過(guò)程中,被transient修飾的變量將不會(huì)被恢復(fù)其序列化前的值。相反,它們會(huì)被初始化為默認(rèn)值(例如,數(shù)值類(lèi)型的變量會(huì)被初始化為0或false,對(duì)象類(lèi)型的變量會(huì)被初始化為null)。因此,開(kāi)發(fā)者需要在反序列化后,根據(jù)需要手動(dòng)為這些變量賦值。
二者對(duì)比表格
| 關(guān)鍵字 | 定義 | 序列化行為 | 反序列化影響 |
|---|---|---|---|
| static | 修飾類(lèi)變量或類(lèi)方法,屬于類(lèi)本身 | 不被序列化 | 保持JVM中的當(dāng)前值 |
| transient | 修飾成員變量,指示在序列化時(shí)忽略該變量 | 被忽略,不被序列化 | 被初始化為默認(rèn)值 |
總結(jié)
到此這篇關(guān)于Java中序列化與反序列化的文章就介紹到這了,更多相關(guān)Java序列化與反序列化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java中Scanner的常用方法總結(jié)(一次學(xué)懂)
這篇文章主要給大家介紹了關(guān)于Java中Scanner常用方法的相關(guān)資料,Java中的Scanner是一個(gè)用于讀取用戶(hù)輸入的類(lèi),它可以讀取各種類(lèi)型的數(shù)據(jù),包括整數(shù)、浮點(diǎn)數(shù)、字符串等等,需要的朋友可以參考下2023-11-11
SpringBoot如何使用mail實(shí)現(xiàn)登錄郵箱驗(yàn)證
在實(shí)際的開(kāi)發(fā)當(dāng)中,不少的場(chǎng)景中需要我們使用更加安全的認(rèn)證方式,同時(shí)也為了防止一些用戶(hù)惡意注冊(cè),我們可能會(huì)需要用戶(hù)使用一些可以證明個(gè)人身份的注冊(cè)方式,如短信驗(yàn)證、郵箱驗(yàn)證等,這篇文章主要介紹了SpringBoot如何使用mail實(shí)現(xiàn)登錄郵箱驗(yàn)證,需要的朋友可以參考下2024-06-06
java多線程Thread的實(shí)現(xiàn)方法代碼詳解
這篇文章主要介紹了java多線程Thread的實(shí)現(xiàn)方法代碼詳解,涉及start(),run(),stop(),interrupt(),isInterrupted(),join()和join(long millis)等方法的介紹,具有一定借鑒價(jià)值,需要的朋友可以了解下。2017-11-11
java異步調(diào)用Feign接口空指針問(wèn)題解決
這篇文章主要為大家介紹了java異步調(diào)用Feign接口空指針問(wèn)題解決方法詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
SpringBoot集成Shiro進(jìn)行權(quán)限控制和管理的示例
這篇文章主要介紹了SpringBoot集成Shiro進(jìn)行權(quán)限控制和管理的示例,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03
解決springboot遇到autowire注入為null的問(wèn)題
這篇文章主要介紹了解決springboot遇到autowire注入為null的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-03-03
Eclipse創(chuàng)建JavaWeb工程的完整步驟記錄
很多新手不知道Eclipse怎么創(chuàng)建Java Web項(xiàng)目,一起來(lái)看看吧,這篇文章主要給大家介紹了關(guān)于Eclipse創(chuàng)建JavaWeb工程的完整步驟,需要的朋友可以參考下2023-10-10
Java超詳細(xì)教你寫(xiě)一個(gè)網(wǎng)絡(luò)購(gòu)書(shū)系統(tǒng)案例
這篇文章主要介紹了怎么用Java來(lái)寫(xiě)一個(gè)購(gòu)書(shū)系統(tǒng),購(gòu)買(mǎi)書(shū)籍主要需要每本書(shū)的編號(hào)、書(shū)名、單價(jià)、庫(kù)存屬性,能夠讓客戶(hù)通過(guò)編號(hào)來(lái)選書(shū),感興趣的朋友跟隨文章往下看看吧2022-03-03
idea如何在service窗口中顯示多個(gè)服務(wù)
這篇文章主要介紹了idea如何在service窗口中顯示多個(gè)服務(wù)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-09-09
如何基于SpringMVC實(shí)現(xiàn)斷點(diǎn)續(xù)傳(HTTP)
這篇文章主要介紹了如何基于SpringMVC實(shí)現(xiàn)斷點(diǎn)續(xù)傳(HTTP),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-01-01

