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

使用記事本打開,依稀可以看到點什么,但明顯不是txt格式的文本。

下面我們將這個文件反序列化至程序中并打印出來:
/**
* 讀取序列化文件,并通過反序列化恢復(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ù)(即序列化)的順序一致,否則會有異常
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
對象輸出流
Car(name=xiaomi su7, type=Pro, price=null)
Java 序列化與反序列化注意事項:
- 讀寫順序要一致。
- 序列化與反序列化對象要實現(xiàn) Serializable 接口。
- 序列化中的類中,建議添加SerialVersionUID,為了提高版本的兼容性。
- 序列化對象時,默認將里面所有的屬性都進行序列化,除了stati、transient 修飾的成員。
- 序列化對象時,要求里面屬性的類型也需要實現(xiàn)序列化接口。
- 序列化具備可繼承性,也就是如果某類已經(jīng)實現(xiàn)了序列化,則它的所有子類也默認實現(xiàn)了序列化。
TIPS:static 和 transient 在序列化中的區(qū)別
static關(guān)鍵字
- 定義:static關(guān)鍵字用于修飾類的成員變量和方法,使其成為類變量或類方法,而不是實例變量或?qū)嵗椒ān愖兞繉儆陬惐旧?,而非類的任何特定實例?/li>
- 序列化行為:在序列化過程中,被static修飾的變量(即類變量)不會被序列化。這是因為static變量是類級別的,它們不屬于任何特定的對象實例,而是由所有實例共享。因此,序列化一個對象時,不會包含其類變量的值,因為類變量的值在JVM中是全局的,與特定的序列化實例無關(guān)。
- 反序列化影響:反序列化一個對象時,被static修飾的變量會保持其在JVM中的當(dāng)前值,而不是從序列化數(shù)據(jù)中恢復(fù)。這是因為靜態(tài)變量是全局的,不受單個對象實例的序列化/反序列化過程影響。
transient關(guān)鍵字
- 定義:transient關(guān)鍵字用于修飾類的成員變量,以指示該變量在序列化時不應(yīng)被序列化。
- 序列化行為:當(dāng)一個對象被序列化時,所有非transient修飾的成員變量都會被序列化,而transient修飾的變量則會被忽略。這意味著,在序列化后的數(shù)據(jù)中,不會包含任何被transient修飾的變量的值。
- 反序列化影響:在反序列化過程中,被transient修飾的變量將不會被恢復(fù)其序列化前的值。相反,它們會被初始化為默認值(例如,數(shù)值類型的變量會被初始化為0或false,對象類型的變量會被初始化為null)。因此,開發(fā)者需要在反序列化后,根據(jù)需要手動為這些變量賦值。
二者對比表格
| 關(guān)鍵字 | 定義 | 序列化行為 | 反序列化影響 |
|---|---|---|---|
| static | 修飾類變量或類方法,屬于類本身 | 不被序列化 | 保持JVM中的當(dāng)前值 |
| transient | 修飾成員變量,指示在序列化時忽略該變量 | 被忽略,不被序列化 | 被初始化為默認值 |
總結(jié)
到此這篇關(guān)于Java中序列化與反序列化的文章就介紹到這了,更多相關(guān)Java序列化與反序列化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java動態(tài)導(dǎo)出excel壓縮成zip下載的方法
這篇文章主要為大家詳細介紹了java動態(tài)導(dǎo)出excel壓縮成zip下載的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-07-07
SpringBoot 整合 Lettuce Redis的實現(xiàn)方法
這篇文章主要介紹了SpringBoot 整合 Lettuce Redis的實現(xiàn)方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07
SpringBoot+Swagger打造美觀API文檔的實戰(zhàn)指南
在現(xiàn)代 Web 開發(fā)中,API 文檔是開發(fā)者和團隊協(xié)作的重要組成部分,本文將詳細介紹如何在 SpringBoot2和SpringBoot3中集成Swagger,并展示多種接口文檔風(fēng)格,幫助開發(fā)者選擇適合自己項目的展示方式2025-08-08
關(guān)于spring?boot使用?jdbc+mysql?連接的問題
這篇文章主要介紹了spring?boot使用?jdbc+mysql?連接,在這里mysql?8.x版本驅(qū)動包,要使用?com.mysql.cj.jdbc.Driver作為驅(qū)動類,文中給大家詳細介紹,需要的朋友可以參考下2022-03-03
學(xué)習(xí)Java之如何正確地向上轉(zhuǎn)型與向下轉(zhuǎn)型
面向?qū)ο蟮牡谌齻€特征是多態(tài),實現(xiàn)多態(tài)有三個必要條件:繼承、方法重寫和向上轉(zhuǎn)型,在學(xué)習(xí)多態(tài)之前,我們還要先學(xué)習(xí)Java的類型轉(zhuǎn)換,本篇文章就來帶大家認識什么是類型轉(zhuǎn)換,看看類型轉(zhuǎn)換都有哪幾種情況,以及如何避免類型轉(zhuǎn)換時出現(xiàn)異常2023-05-05

