Java中序列化和反序列化的完整講解
一、序列化
1.1.Serialization(序列化):
將java對(duì)象以一連串的字節(jié)保存在磁盤(pán)文件中的過(guò)程,也可以說(shuō)是保存java對(duì)象狀態(tài)的過(guò)程。序列化可以將數(shù)據(jù)永久保存在磁盤(pán)上(通常保存在文件中)
1.2.deserialization(反序列化)
將保存在磁盤(pán)文件中的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ū)ο蟮拈_(kāi)發(fā)方式,一切都是java對(duì)象,想要在網(wǎng)絡(luò)中傳輸java對(duì)象,可以使用序列化和反序列化去實(shí)現(xiàn),發(fā)送發(fā)需要將java對(duì)象轉(zhuǎn)換為字節(jié)序列,然后在網(wǎng)絡(luò)上傳送,接收方收到字符序列后,會(huì)通過(guò)反序列化將字節(jié)序列恢復(fù)成java對(duì)象。
java序列化的優(yōu)點(diǎn):
- 實(shí)現(xiàn)了數(shù)據(jù)的持久化,通過(guò)序列化可以把數(shù)據(jù)持久地保存在硬盤(pán)上(磁盤(pán)文件)。
- 利用序列化實(shí)現(xiàn)遠(yuǎn)程通信,在網(wǎng)絡(luò)上傳輸字節(jié)序列
三、序列化和反序列化地實(shí)現(xiàn)
3.1.JDK類(lèi)庫(kù)提供的序列化API
java.io.ObjectOutputStream
表示對(duì)象輸出流,其中writeObject(Object obj)方法可以將給定參數(shù)的obj對(duì)象進(jìn)行序列化,將轉(zhuǎn)換的一連串的字節(jié)序列寫(xiě)到指定的目標(biāo)輸出流中。
java.io.ObjectInputStream
該類(lèi)表示對(duì)象輸入流,該類(lèi)下的readObject(Object obj)方法會(huì)從源輸入流中讀取字節(jié)序列,并將它反序列化為一個(gè)java對(duì)象并返回
3.2.序列化要求
實(shí)現(xiàn)序列化的類(lèi)對(duì)象必須實(shí)現(xiàn)了Serializable類(lèi)或Externalizable類(lèi)才能被序列化,否則會(huì)拋出異常
3.3.實(shí)現(xiàn)java序列化和反序列化的三種方法
現(xiàn)在要對(duì)student類(lèi)進(jìn)行序列化和反序列化,遵循以下方法:
3.3.1.方法一
若student類(lèi)實(shí)現(xiàn)了serializable接口,則可以通過(guò)objectOutputstream和objectinputstream默認(rèn)的序列化和反序列化方式,對(duì)非transient的實(shí)例變量進(jìn)行序列化和反序列化
3.3.2. 方法二
若student類(lèi)實(shí)現(xiàn)了serializable接口,并且定義了writeObject(objectOutputStream out)和
readObject(objectinputStream in)方法,則可以直接調(diào)用student類(lèi)的兩種方法進(jìn)行序列化和反序列化
3.3.3.方法三
若student類(lèi)實(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); // 第二步,通過(guò)輸出流對(duì)象的 writeObject()方法寫(xiě)對(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ì)象輸出流寫(xiě)入對(duì)象的順序與對(duì)象輸入流讀取對(duì)象的順序一致
四、CustomerForm 類(lèi)序列化和反序列化演示
4.1.先創(chuàng)建一個(gè)實(shí)現(xiàn)了serializable接口的CustomerForm 類(lèi)
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類(lèi)的對(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); // 序列化 第二步,通過(guò)輸出流對(duì)象的 writeObject()方法寫(xiě)對(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(); } }
序列化之后本地文件訪問(wèn)結(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); // 序列化 第二步,通過(guò)輸出流對(duì)象的 writeObject()方法寫(xiě)對(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)容沒(mé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è)方法抽象出來(lái)。Externalizable沒(méi)有serializable的限制,static和transient關(guān)鍵字修飾的屬性也能進(jìn)行序列化。
5.1.Externalizable 的不同點(diǎn)
Externalizable沒(méi)有serializable的限制,transient關(guān)鍵字修飾的屬性也能進(jìn)行序列化
5.2.CustomerForm 實(shí)現(xiàn)類(lèi) 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 類(lèi) 后需要重寫(xiě) 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); // 序列化 第二步,通過(guò)輸出流對(duì)象的 writeObject()方法寫(xiě)對(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é)
序列化和反序列化可以過(guò)這兩種方式來(lái)實(shí)現(xiàn):
1、Bean對(duì)象可以通過(guò)Serializable接口實(shí)現(xiàn)序列化與反序列化
2、Bean對(duì)象可以通過(guò)Externalizable接口實(shí)現(xiàn)序列化與反序列化
不同點(diǎn)
1、Externalizable接口實(shí)現(xiàn)實(shí)例化,需要重寫(xiě) writeExternal()、readExternal() 方法
2、Externalizable接口實(shí)現(xiàn)實(shí)例化,不受 transient關(guān)鍵字限制
3、Serializable接口實(shí)現(xiàn),transient關(guān)鍵字修飾后,不能參與實(shí)例化
4、被static修飾的變量(都)不能被實(shí)例化
原因:被static修飾的屬性是所有類(lèi)共享的,如果可以序列化,就會(huì)出現(xiàn)下面的情況,當(dāng)我們序列化某個(gè)類(lèi)的一個(gè)對(duì)象到某個(gè)文件后,這個(gè)文件中的對(duì)象的那個(gè)被static修飾的屬性值會(huì)固定下來(lái),當(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)文章
java.util.Collections類(lèi)—emptyList()方法的使用
這篇文章主要介紹了java.util.Collections類(lèi)—emptyList()方法的使用,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11微服務(wù)實(shí)戰(zhàn)之怎樣提升springboot服務(wù)吞吐量
這篇文章主要介紹了微服務(wù)實(shí)戰(zhàn)之怎樣提升springboot服務(wù)吞吐量方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08java使用枚舉封裝錯(cuò)誤碼及錯(cuò)誤信息詳解
這篇文章主要介紹了java使用枚舉封裝錯(cuò)誤碼及錯(cuò)誤信息,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-12-12Java OpenCV實(shí)現(xiàn)圖像鏡像翻轉(zhuǎn)效果
這篇文章主要為大家詳細(xì)介紹了Java OpenCV實(shí)現(xiàn)圖像鏡像翻轉(zhuǎn)效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07postgresql 實(shí)現(xiàn)16進(jìn)制字符串轉(zhuǎn)10進(jìn)制數(shù)字
這篇文章主要介紹了postgresql 實(shí)現(xiàn)16進(jìn)制字符串轉(zhuǎn)10進(jìn)制數(shù)字操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-02-02