Java中序列化和反序列化的完整講解
一、序列化
1.1.Serialization(序列化):
將java對象以一連串的字節(jié)保存在磁盤文件中的過程,也可以說是保存java對象狀態(tài)的過程。序列化可以將數(shù)據(jù)永久保存在磁盤上(通常保存在文件中)
1.2.deserialization(反序列化)
將保存在磁盤文件中的java字節(jié)碼重新轉(zhuǎn)換成java對象稱為反序列化
二、序列化和反序列化的應(yīng)用
兩個進程在遠程通信時,可以發(fā)送多種數(shù)據(jù),包括文本、圖片、音頻、視頻等,這些數(shù)據(jù)都是以二進制序列的形式在網(wǎng)絡(luò)上傳輸。
java是面向?qū)ο蟮拈_發(fā)方式,一切都是java對象,想要在網(wǎng)絡(luò)中傳輸java對象,可以使用序列化和反序列化去實現(xiàn),發(fā)送發(fā)需要將java對象轉(zhuǎn)換為字節(jié)序列,然后在網(wǎng)絡(luò)上傳送,接收方收到字符序列后,會通過反序列化將字節(jié)序列恢復(fù)成java對象。
java序列化的優(yōu)點:
- 實現(xiàn)了數(shù)據(jù)的持久化,通過序列化可以把數(shù)據(jù)持久地保存在硬盤上(磁盤文件)。
- 利用序列化實現(xiàn)遠程通信,在網(wǎng)絡(luò)上傳輸字節(jié)序列
三、序列化和反序列化地實現(xiàn)
3.1.JDK類庫提供的序列化API
java.io.ObjectOutputStream
表示對象輸出流,其中writeObject(Object obj)方法可以將給定參數(shù)的obj對象進行序列化,將轉(zhuǎn)換的一連串的字節(jié)序列寫到指定的目標(biāo)輸出流中。
java.io.ObjectInputStream
該類表示對象輸入流,該類下的readObject(Object obj)方法會從源輸入流中讀取字節(jié)序列,并將它反序列化為一個java對象并返回
3.2.序列化要求
實現(xiàn)序列化的類對象必須實現(xiàn)了Serializable類或Externalizable類才能被序列化,否則會拋出異常
3.3.實現(xiàn)java序列化和反序列化的三種方法
現(xiàn)在要對student類進行序列化和反序列化,遵循以下方法:
3.3.1.方法一
若student類實現(xiàn)了serializable接口,則可以通過objectOutputstream和objectinputstream默認的序列化和反序列化方式,對非transient的實例變量進行序列化和反序列化
3.3.2. 方法二
若student類實現(xiàn)了serializable接口,并且定義了writeObject(objectOutputStream out)和
readObject(objectinputStream in)方法,則可以直接調(diào)用student類的兩種方法進行序列化和反序列化
3.3.3.方法三
若student類實現(xiàn)了Externalizable接口,則必須實現(xiàn)readExternal(Objectinput in)和writeExternal(Objectoutput out)方法進行序列化和反序列化
3.4.序列化步驟
public static void main(String[] args) {
File filename = new File("E:/Student1.txt");
try {
// 第一步,創(chuàng)建一個輸出流對象,它可以包裝一個輸出流對象,如:文件輸出流
FileOutputStream fileOutputStream = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(fileOutputStream);
// 第二步,通過輸出流對象的 writeObject()方法寫對象,從 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)建文件輸入流對象
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ù),對象輸出流寫入對象的順序與對象輸入流讀取對象的順序一致
四、CustomerForm 類序列化和反序列化演示
4.1.先創(chuàng)建一個實現(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類的對象序列化到CustomerForm.txt 文件(E:/CustomerForm.txt)中,并對文件進行反序列化獲取數(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)建一個輸出流對象,它可以包裝一個輸出流對象,如:文件輸出流
fileOutputStream = new FileOutputStream(file);
out = new ObjectOutputStream(fileOutputStream);
// 序列化 第二步,通過輸出流對象的 writeObject()方法寫對象,從 java 輸出到本地文件 out
out.writeObject(customerForm);
// 反序列化 第一步:創(chuàng)建文件輸入流對象
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)建一個輸出流對象,它可以包裝一個輸出流對象,如:文件輸出流
FileOutputStream fileOutputStream = new FileOutputStream(file);
ObjectOutputStream out = new ObjectOutputStream(fileOutputStream);
// 序列化 第二步,通過輸出流對象的 writeObject()方法寫對象,從 java 輸出到本地文件 out
out.writeObject(customerForm);
// 反序列化 第一步:創(chuàng)建文件輸入流對象
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修飾的屬性不能被實例化
五、Externalizable接口實現(xiàn)序列化與反序列化
Externalizable 可外部化的
Externalizable接口繼承Serializable接口,實現(xiàn)Externalizable接口需要實現(xiàn)readExternal()方法和writeExternal()方法,這兩個方法是抽象方法,對應(yīng)的是serializable接口的readObject()方法和writeObject()方法,可以理解為把serializable的兩個方法抽象出來。Externalizable沒有serializable的限制,static和transient關(guān)鍵字修飾的屬性也能進行序列化。
5.1.Externalizable 的不同點
Externalizable沒有serializable的限制,transient關(guān)鍵字修飾的屬性也能進行序列化
5.2.CustomerForm 實現(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();
}
}實現(xiàn) Externalizable 類 后需要重寫 writeExternal()、readExternal() 方法
5.3.Externalizable 實現(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)建一個輸出流對象,它可以包裝一個輸出流對象,如:文件輸出流
FileOutputStream fileOutputStream = new FileOutputStream(file);
ObjectOutputStream out = new ObjectOutputStream(fileOutputStream);
// 序列化 第二步,通過輸出流對象的 writeObject()方法寫對象,從 java 輸出到本地文件 out
out.writeObject(customerForm);
// 反序列化 第一步:創(chuàng)建文件輸入流對象
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)參與了實例化,被static修飾的變量不能被實例化
總結(jié)
序列化和反序列化可以過這兩種方式來實現(xiàn):
1、Bean對象可以通過Serializable接口實現(xiàn)序列化與反序列化
2、Bean對象可以通過Externalizable接口實現(xiàn)序列化與反序列化
不同點
1、Externalizable接口實現(xiàn)實例化,需要重寫 writeExternal()、readExternal() 方法
2、Externalizable接口實現(xiàn)實例化,不受 transient關(guān)鍵字限制
3、Serializable接口實現(xiàn),transient關(guān)鍵字修飾后,不能參與實例化
4、被static修飾的變量(都)不能被實例化
原因:被static修飾的屬性是所有類共享的,如果可以序列化,就會出現(xiàn)下面的情況,當(dāng)我們序列化某個類的一個對象到某個文件后,這個文件中的對象的那個被static修飾的屬性值會固定下來,當(dāng)另外一個普通的的對象修改了該static屬性后,我們再去反序列化那個文件中的對象,就會得到和后面的對象不同的static屬性值,這顯然違背了static關(guān)鍵字誕生的初衷
到此這篇關(guān)于Java中序列化和反序列化的完整講解的文章就介紹到這了,更多相關(guān)Java序列化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java.util.Collections類—emptyList()方法的使用
這篇文章主要介紹了java.util.Collections類—emptyList()方法的使用,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11
微服務(wù)實戰(zhàn)之怎樣提升springboot服務(wù)吞吐量
這篇文章主要介紹了微服務(wù)實戰(zhàn)之怎樣提升springboot服務(wù)吞吐量方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-08-08
Java OpenCV實現(xiàn)圖像鏡像翻轉(zhuǎn)效果
這篇文章主要為大家詳細介紹了Java OpenCV實現(xiàn)圖像鏡像翻轉(zhuǎn)效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-07-07
postgresql 實現(xiàn)16進制字符串轉(zhuǎn)10進制數(shù)字
這篇文章主要介紹了postgresql 實現(xiàn)16進制字符串轉(zhuǎn)10進制數(shù)字操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-02-02

