Java中序列化和反序列化的完整講解
一、序列化
1.1.Serialization(序列化):
將java對(duì)象以一連串的字節(jié)保存在磁盤文件中的過程,也可以說是保存java對(duì)象狀態(tài)的過程。序列化可以將數(shù)據(jù)永久保存在磁盤上(通常保存在文件中)
1.2.deserialization(反序列化)
將保存在磁盤文件中的java字節(jié)碼重新轉(zhuǎn)換成java對(duì)象稱為反序列化
二、序列化和反序列化的應(yīng)用
兩個(gè)進(jìn)程在遠(yuǎn)程通信時(shí),可以發(fā)送多種數(shù)據(jù),包括文本、圖片、音頻、視頻等,這些數(shù)據(jù)都是以二進(jìn)制序列的形式在網(wǎng)絡(luò)上傳輸。
java是面向?qū)ο蟮拈_發(fā)方式,一切都是java對(duì)象,想要在網(wǎng)絡(luò)中傳輸java對(duì)象,可以使用序列化和反序列化去實(shí)現(xiàn),發(fā)送發(fā)需要將java對(duì)象轉(zhuǎn)換為字節(jié)序列,然后在網(wǎng)絡(luò)上傳送,接收方收到字符序列后,會(huì)通過反序列化將字節(jié)序列恢復(fù)成java對(duì)象。
java序列化的優(yōu)點(diǎn):
- 實(shí)現(xiàn)了數(shù)據(jù)的持久化,通過序列化可以把數(shù)據(jù)持久地保存在硬盤上(磁盤文件)。
- 利用序列化實(shí)現(xiàn)遠(yuǎn)程通信,在網(wǎng)絡(luò)上傳輸字節(jié)序列
三、序列化和反序列化地實(shí)現(xiàn)
3.1.JDK類庫提供的序列化API
java.io.ObjectOutputStream
表示對(duì)象輸出流,其中writeObject(Object obj)方法可以將給定參數(shù)的obj對(duì)象進(jìn)行序列化,將轉(zhuǎn)換的一連串的字節(jié)序列寫到指定的目標(biāo)輸出流中。
java.io.ObjectInputStream
該類表示對(duì)象輸入流,該類下的readObject(Object obj)方法會(huì)從源輸入流中讀取字節(jié)序列,并將它反序列化為一個(gè)java對(duì)象并返回
3.2.序列化要求
實(shí)現(xiàn)序列化的類對(duì)象必須實(shí)現(xiàn)了Serializable類或Externalizable類才能被序列化,否則會(huì)拋出異常
3.3.實(shí)現(xiàn)java序列化和反序列化的三種方法
現(xiàn)在要對(duì)student類進(jìn)行序列化和反序列化,遵循以下方法:
3.3.1.方法一
若student類實(shí)現(xiàn)了serializable接口,則可以通過objectOutputstream和objectinputstream默認(rèn)的序列化和反序列化方式,對(duì)非transient的實(shí)例變量進(jìn)行序列化和反序列化
3.3.2. 方法二
若student類實(shí)現(xiàn)了serializable接口,并且定義了writeObject(objectOutputStream out)和
readObject(objectinputStream in)方法,則可以直接調(diào)用student類的兩種方法進(jìn)行序列化和反序列化
3.3.3.方法三
若student類實(shí)現(xiàn)了Externalizable接口,則必須實(shí)現(xiàn)readExternal(Objectinput in)和writeExternal(Objectoutput out)方法進(jìn)行序列化和反序列化
3.4.序列化步驟
public static void main(String[] args) {
File filename = new File("E:/Student1.txt");
try {
// 第一步,創(chuàng)建一個(gè)輸出流對(duì)象,它可以包裝一個(gè)輸出流對(duì)象,如:文件輸出流
FileOutputStream fileOutputStream = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(fileOutputStream);
// 第二步,通過輸出流對(duì)象的 writeObject()方法寫對(duì)象,從 java 輸出到本地文件 out
out.writeObject("第一次輸出:第一次打卡哦");
out.writeObject("第二次輸出:第二次打卡");
} catch (IOException e) {
e.printStackTrace();
}
}
3.5.反序列化操作
public static void main(String[] args) {
File filename = new File("E:/Student1.txt");
try {
// 第一步:創(chuàng)建文件輸入流對(duì)象
FileInputStream fileInputStream = new FileInputStream(filename);
ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
// 調(diào)用readObject()方法,本地文件讀取數(shù)據(jù),輸入到程序當(dāng)中
System.out.println(inputStream.readObject());
System.out.println(inputStream.readObject());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}為了保證正確讀取數(shù)據(jù),對(duì)象輸出流寫入對(duì)象的順序與對(duì)象輸入流讀取對(duì)象的順序一致
四、CustomerForm 類序列化和反序列化演示
4.1.先創(chuàng)建一個(gè)實(shí)現(xiàn)了serializable接口的CustomerForm 類
Serializable (/?s??ri??la?z?bl/) 序列化
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* <p>Java class for attachmentForm complex type.
*
*/
@Data
public class CustomerForm implements Serializable {
protected String requestCode;
protected List<Customer> reqData;
/**
* transient 修飾的屬性不能序列化 *
*/
private transient String msg;
private static String name = "被static修飾的name";
}把CustomerForm類的對(duì)象序列化到CustomerForm.txt 文件(E:/CustomerForm.txt)中,并對(duì)文件進(jìn)行反序列化獲取數(shù)據(jù):
public static void main(String[] args) {
FileOutputStream fileOutputStream = null;
ObjectOutputStream out = null;
FileInputStream fileInputStream = null;
ObjectInputStream inputStream = null;
try {
File file = new File("E:/CustomerForm.txt");
if (file.exists()){
System.out.println("文件已存在");
} else {
file.createNewFile();
}
CustomerForm customerForm = new CustomerForm();
customerForm.setRequestCode("1-2-3-4-5-6-7-8-9");
// 序列化 第一步,創(chuàng)建一個(gè)輸出流對(duì)象,它可以包裝一個(gè)輸出流對(duì)象,如:文件輸出流
fileOutputStream = new FileOutputStream(file);
out = new ObjectOutputStream(fileOutputStream);
// 序列化 第二步,通過輸出流對(duì)象的 writeObject()方法寫對(duì)象,從 java 輸出到本地文件 out
out.writeObject(customerForm);
// 反序列化 第一步:創(chuàng)建文件輸入流對(duì)象
fileInputStream = new FileInputStream(file);
inputStream = new ObjectInputStream(fileInputStream);
// 反序列化 第二步:調(diào)用readObject()方法,本地文件讀取數(shù)據(jù),輸入到程序當(dāng)中
System.out.println(inputStream.readObject());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
out.close();
fileOutputStream.close();
fileInputStream.close();
inputStream.close();
}
}序列化之后本地文件訪問結(jié)果:

反序列化結(jié)果:

4.2.transient 關(guān)鍵字
transient ( /?træn?(?)nt/)瞬變的
transient 修飾的屬性不能參加序列化
以上程序重新執(zhí)行:
public static void main(String[] args) {
try {
File file = new File("E:/CustomerForm.txt");
if (file.exists()){
System.out.println("文件已存在");
} else {
file.createNewFile();
}
CustomerForm customerForm = new CustomerForm();
customerForm.setRequestCode("1-2-3-4-5-6-7-8-9");
/**
* transient 修飾的屬性不能序列化 *
*/
customerForm.setMsg("此屬性不能參加序列化");
// 序列化 第一步,創(chuàng)建一個(gè)輸出流對(duì)象,它可以包裝一個(gè)輸出流對(duì)象,如:文件輸出流
FileOutputStream fileOutputStream = new FileOutputStream(file);
ObjectOutputStream out = new ObjectOutputStream(fileOutputStream);
// 序列化 第二步,通過輸出流對(duì)象的 writeObject()方法寫對(duì)象,從 java 輸出到本地文件 out
out.writeObject(customerForm);
// 反序列化 第一步:創(chuàng)建文件輸入流對(duì)象
FileInputStream fileInputStream = new FileInputStream(file);
ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
// 反序列化 第二步:調(diào)用readObject()方法,本地文件讀取數(shù)據(jù),輸入到程序當(dāng)中
System.out.println(inputStream.readObject());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}執(zhí)行結(jié)果:

從結(jié)果看出 被transient關(guān)鍵字修飾的變量 msg 賦值內(nèi)容沒有被序列化,被修改為null,被static修飾的屬性不能被實(shí)例化
五、Externalizable接口實(shí)現(xiàn)序列化與反序列化
Externalizable 可外部化的
Externalizable接口繼承Serializable接口,實(shí)現(xiàn)Externalizable接口需要實(shí)現(xiàn)readExternal()方法和writeExternal()方法,這兩個(gè)方法是抽象方法,對(duì)應(yīng)的是serializable接口的readObject()方法和writeObject()方法,可以理解為把serializable的兩個(gè)方法抽象出來。Externalizable沒有serializable的限制,static和transient關(guān)鍵字修飾的屬性也能進(jìn)行序列化。
5.1.Externalizable 的不同點(diǎn)
Externalizable沒有serializable的限制,transient關(guān)鍵字修飾的屬性也能進(jìn)行序列化
5.2.CustomerForm 實(shí)現(xiàn)類 Externalizable
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* <p>Java class for attachmentForm complex type.
*
*/
@Data
public class CustomerForm implements Externalizable{
protected String requestCode;
protected List<Customer> reqData;
private transient String msg;
private static String name = "被static修飾的name";
@Override
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(requestCode);
out.writeObject(msg);
out.writeObject(reqData);
}
@Override
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
requestCode = (String) in.readObject();
msg = (String) in.readObject();
reqData = (List<Customer>) in.readObject();
}
}實(shí)現(xiàn) Externalizable 類 后需要重寫 writeExternal()、readExternal() 方法
5.3.Externalizable 實(shí)現(xiàn)序列化和反序列化
public static void main(String[] args) {
try {
File file = new File("E:/CustomerForm.txt");
if (file.exists()){
System.out.println("文件已存在");
} else {
file.createNewFile();
}
CustomerForm customerForm = new CustomerForm();
customerForm.setRequestCode("1-2-3-4-5-6-7-8-9");
customerForm.setMsg("該屬性被transient修飾");
// 序列化 第一步,創(chuàng)建一個(gè)輸出流對(duì)象,它可以包裝一個(gè)輸出流對(duì)象,如:文件輸出流
FileOutputStream fileOutputStream = new FileOutputStream(file);
ObjectOutputStream out = new ObjectOutputStream(fileOutputStream);
// 序列化 第二步,通過輸出流對(duì)象的 writeObject()方法寫對(duì)象,從 java 輸出到本地文件 out
out.writeObject(customerForm);
// 反序列化 第一步:創(chuàng)建文件輸入流對(duì)象
FileInputStream fileInputStream = new FileInputStream(file);
ObjectInputStream inputStream = new ObjectInputStream(fileInputStream);
// 反序列化 第二步:調(diào)用readObject()方法,本地文件讀取數(shù)據(jù),輸入到程序當(dāng)中
System.out.println(inputStream.readObject());
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}執(zhí)行結(jié)果:

可以看到被transient關(guān)鍵字修飾的變量msg已經(jīng)參與了實(shí)例化,被static修飾的變量不能被實(shí)例化
總結(jié)
序列化和反序列化可以過這兩種方式來實(shí)現(xiàn):
1、Bean對(duì)象可以通過Serializable接口實(shí)現(xiàn)序列化與反序列化
2、Bean對(duì)象可以通過Externalizable接口實(shí)現(xiàn)序列化與反序列化
不同點(diǎn)
1、Externalizable接口實(shí)現(xiàn)實(shí)例化,需要重寫 writeExternal()、readExternal() 方法
2、Externalizable接口實(shí)現(xiàn)實(shí)例化,不受 transient關(guān)鍵字限制
3、Serializable接口實(shí)現(xiàn),transient關(guān)鍵字修飾后,不能參與實(shí)例化
4、被static修飾的變量(都)不能被實(shí)例化
原因:被static修飾的屬性是所有類共享的,如果可以序列化,就會(huì)出現(xiàn)下面的情況,當(dāng)我們序列化某個(gè)類的一個(gè)對(duì)象到某個(gè)文件后,這個(gè)文件中的對(duì)象的那個(gè)被static修飾的屬性值會(huì)固定下來,當(dāng)另外一個(gè)普通的的對(duì)象修改了該static屬性后,我們?cè)偃シ葱蛄谢莻€(gè)文件中的對(duì)象,就會(huì)得到和后面的對(duì)象不同的static屬性值,這顯然違背了static關(guān)鍵字誕生的初衷
到此這篇關(guān)于Java中序列化和反序列化的完整講解的文章就介紹到這了,更多相關(guān)Java序列化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mybatis(mybatis-plus)映射文件(XML文件)中特殊字符轉(zhuǎn)義的實(shí)現(xiàn)
XML 文件在解析時(shí)會(huì)將五種特殊字符進(jìn)行轉(zhuǎn)義,本文主要介紹了mybatis(mybatis-plus)映射文件(XML文件)中特殊字符轉(zhuǎn)義的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下2023-12-12
spring boot在啟動(dòng)項(xiàng)目之后執(zhí)行的實(shí)現(xiàn)方法
在開發(fā)時(shí)有時(shí)候需要在整個(gè)應(yīng)用開始運(yùn)行時(shí)執(zhí)行一些特定代碼,比如初始化環(huán)境,下面這篇文章就來給大家介紹了關(guān)于spring boot在啟動(dòng)項(xiàng)目之后執(zhí)行自己要執(zhí)行的東西的實(shí)現(xiàn)方法,文中給出了詳細(xì)的示例代碼,需要的朋友可以參考下。2017-09-09
使用SpringBoot整合Activiti6工作流的操作方法
這篇文章主要介紹了使用SpringBoot整合Activiti6工作流,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-07-07
如何在 Java 中利用 redis 實(shí)現(xiàn) LBS 服務(wù)
基于位置的服務(wù),是指通過電信移動(dòng)運(yùn)營(yíng)商的無線電通訊網(wǎng)絡(luò)或外部定位方式,獲取移動(dòng)終端用戶的位置信息,在GIS平臺(tái)的支持下,為用戶提供相應(yīng)服務(wù)的一種增值業(yè)務(wù)。下面我們來一起學(xué)習(xí)一下吧2019-06-06
SpringBoot中打印SQL語句的幾種方法實(shí)現(xiàn)
本文主要介紹了SpringBoot中打印SQL語句的幾種方法實(shí)現(xiàn),,通過打印SQL語句可以幫助開發(fā)人員快速了解數(shù)據(jù)庫的操作情況,進(jìn)而進(jìn)行性能分析和調(diào)試,感興趣的可以了解一下2023-11-11
Mybatis超級(jí)強(qiáng)大的動(dòng)態(tài)SQL語句大全
MyBatis的動(dòng)態(tài)SQL是基于OGNL表達(dá)式的,它可以幫助我們方便的在SQL語句中實(shí)現(xiàn)某些邏輯,下面這篇文章主要給大家介紹了關(guān)于Mybatis超級(jí)強(qiáng)大的動(dòng)態(tài)SQL語句的相關(guān)資料,需要的朋友可以參考下2022-05-05
Java中Comparable和Comparator兩種比較器的區(qū)別詳解
這篇文章主要介紹了Java中Comparable和Comparator兩種比較器的區(qū)別詳解,Comparable接口將比較代碼嵌入自身類中,像Integer、String等這些基本類型的JAVA封裝類都已經(jīng)實(shí)現(xiàn)了Comparable接口,這些類對(duì)象本身就支持和自己比較,需要的朋友可以參考下2023-09-09
一文深入分析java.lang.ClassNotFoundException異常
這篇文章主要給大家介紹了關(guān)于java.lang.ClassNotFoundException異常的相關(guān)資料,java.lang.ClassNotFoundException是Java編程時(shí)經(jīng)常會(huì)遇到的一個(gè)異常,它表示JVM在嘗試加載某個(gè)類時(shí)未能找到該類,需要的朋友可以參考下2023-10-10

