攝影工作室網(wǎng)站模板網(wǎng)絡(luò)優(yōu)化工程師主要負(fù)責(zé)什么工作
什么是序列化?反序列化? 場(chǎng)景使用? 怎么實(shí)現(xiàn)?
今天我來(lái)帶你掃掃盲, 關(guān)于什么是序列化?反序列化? 場(chǎng)景使用? 怎么實(shí)現(xiàn)??? 或許我們平時(shí)大概知道, 嗯,序列化,就是實(shí)現(xiàn) Serializable 接口, 那再問你,怎么實(shí)現(xiàn)的, 或許你就有點(diǎn)懵逼,這篇, 讓我們來(lái) look 一 look 吧。
一.什么是序列化?反序列化?
序列化:指把堆內(nèi)存中的 Java 對(duì)象數(shù)據(jù),通過某種方式把對(duì)象存儲(chǔ)到磁盤文件中或者傳遞給其他網(wǎng)絡(luò)節(jié)點(diǎn)(在網(wǎng)絡(luò)上傳輸)。這個(gè)過程稱為序列化。通俗來(lái)說就是將數(shù)據(jù)結(jié)構(gòu)或?qū)ο筠D(zhuǎn)換成二進(jìn)制數(shù)據(jù)流的過程
反序列化:把磁盤文件中的對(duì)象數(shù)據(jù)或者把網(wǎng)絡(luò)節(jié)點(diǎn)上的對(duì)象數(shù)據(jù),恢復(fù)成Java對(duì)象模型的過程。也就是將在序列化過程中所生成的二進(jìn)制數(shù)據(jù)流轉(zhuǎn)換成數(shù)據(jù)結(jié)構(gòu)或者對(duì)象的過程
二. 為什么要序列化和反序列化?什么場(chǎng)景使用?
①、在分布式系統(tǒng)中,此時(shí)需要把對(duì)象在網(wǎng)絡(luò)上傳輸,就得把對(duì)象數(shù)據(jù)轉(zhuǎn)換為二進(jìn)制形式,需要共享的數(shù)據(jù)的 JavaBean 對(duì)象,都得做序列化。
②、服務(wù)器鈍化:如果服務(wù)器發(fā)現(xiàn)某些對(duì)象好久沒活動(dòng)了,那么服務(wù)器就會(huì)把這些內(nèi)存中的對(duì)象持久化在本地磁盤文件中(Java對(duì)象轉(zhuǎn)換為二進(jìn)制文件);如果服務(wù)器發(fā)現(xiàn)某些對(duì)象需要活動(dòng)時(shí),先去內(nèi)存中尋找,找不到再去磁盤文件中反序列化我們的對(duì)象數(shù)據(jù),恢復(fù)成 Java 對(duì)象。這樣能節(jié)省服務(wù)器內(nèi)存。
我們想一想, 當(dāng)兩個(gè)進(jìn)程進(jìn)行遠(yuǎn)程通信時(shí),可以相互發(fā)送各種類型的數(shù)據(jù),包括文本、圖片、音頻、視頻等,這些如何實(shí)現(xiàn)的呢?
沒錯(cuò),這其中就用到了 Java序列化與反序列化了 ~~~ 這些數(shù)據(jù)都會(huì)以二進(jìn)制序列的形式在網(wǎng)絡(luò)上傳送。
一句話概括就是 , 發(fā)送方 發(fā)送數(shù)據(jù)需要把這個(gè)Java對(duì)象轉(zhuǎn)換為字節(jié)序列,然后在網(wǎng)絡(luò)上傳送 ; 然后接收方需要從字節(jié)序列中恢復(fù)出Java對(duì)象。從而達(dá)到傳送的目的~~
總的來(lái)說: 使用場(chǎng)景歸納如下:
1.把內(nèi)存中的對(duì)象保存到一個(gè)文件中或者數(shù)據(jù)庫(kù)中;
2. 網(wǎng)絡(luò)上傳送對(duì)象;
3.通過RMI傳輸對(duì)象;(RMI->Remote Method Invocation 遠(yuǎn)程方法調(diào)用)
小錦囊: RMI是什么?
RMI(Remote Method Invocation,遠(yuǎn)程方法調(diào)用)是bai用Java在JDK1.1中實(shí)現(xiàn)的,它du大大增強(qiáng)了Java開發(fā)分布式應(yīng)用zhi的能力。Java作為一種風(fēng)靡一dao時(shí)的網(wǎng)絡(luò)開發(fā)語(yǔ)言,其巨大的威力就體現(xiàn)在它強(qiáng)大的開發(fā)分布式網(wǎng)絡(luò)應(yīng)用的能力上,而RMI就是開發(fā)百分之百純Java的網(wǎng)絡(luò)分布式應(yīng)用系統(tǒng)的核心解決方案之一。其實(shí)它可以被看作是RPC的Java版本。但是傳統(tǒng)RPC并不能很好地應(yīng)用于分布式對(duì)象系統(tǒng)。而Java RMI 則支持存儲(chǔ)于不同地址空間的程序級(jí)對(duì)象之間彼此進(jìn)行通信,實(shí)現(xiàn)遠(yuǎn)程對(duì)象之間的無(wú)縫遠(yuǎn)程調(diào)用。
三. 序列化有什么作用?好處?
1.描述數(shù)據(jù)的傳輸格式,這樣可以方便自己組織數(shù)據(jù)傳輸格式,以至于避免一些麻煩及錯(cuò)誤
2.如果是跨平臺(tái)的序列化,則發(fā)送方序列化后,接收方可以用任何其支持的平臺(tái)反序列化成相應(yīng)的版本,比如 Java序列化后, 用.net、phython等反序列化
3.利用序列化實(shí)現(xiàn)遠(yuǎn)程通信,即在網(wǎng)絡(luò)上傳送對(duì)象的字節(jié)序列。
4.為了解決對(duì)象流讀寫操作時(shí)可能引發(fā)的問題(如果不進(jìn)行序列化,可能會(huì)存在數(shù)據(jù)亂序的問題)
5.實(shí)現(xiàn)了數(shù)據(jù)的持久化,通過序列化可以把數(shù)據(jù)永久地保存到硬盤上(通常存放在文件里),
6.序列化除了能夠?qū)崿F(xiàn)對(duì)象的持久化之外,還能夠用于對(duì)象的深度克隆
四.如何實(shí)現(xiàn) 序列化和反序列化?
Java API提供了對(duì)序列化的支持
1、JDK類庫(kù)中序列化和反序列化API
(1)java.io.ObjectOutputStream:表示對(duì)象輸出流;
它的writeObject(Object obj)方法可以對(duì)參數(shù)指定的obj對(duì)象進(jìn)行序列化,把得到的字節(jié)序列寫到一個(gè)目標(biāo)輸出流中;
(2)java.io.ObjectInputStream:表示對(duì)象輸入流;
它的readObject()方法源輸入流中讀取字節(jié)序列,再把它們反序列化成為一個(gè)對(duì)象,并將其返回;
序列化圖示:
反序列化圖示:
- 實(shí)現(xiàn)序列化的前提
必須實(shí)現(xiàn)了Serializable或Externalizable接口的類的對(duì)象才能被序列化,否則拋出異常!
- 實(shí)現(xiàn)代碼
第一步:創(chuàng)建一個(gè) JavaBean 對(duì)象
package com.czxy.domain;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;/*** @author 煌sir@itcast.cn* @version 1.0**/
@Table(name = "t_area")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Area implements Serializable {@Idprivate Integer id;private String name;
}
第二步:在測(cè)試類使用 ObjectOutputStream 對(duì)象實(shí)現(xiàn)序列化
@Testpublic void run1() throws Exception {Area a = new Area(1, "煌sir6666");File file = new File("E:\\file\\student.txt"); //文件位置file.createNewFile(); //自動(dòng)創(chuàng)建文件FileOutputStream fos = new FileOutputStream(file);ObjectOutputStream oos = new ObjectOutputStream(fos);oos.writeObject(a);oos.flush();oos.close();fos.close();}
運(yùn)行成功后, 在存儲(chǔ)文件中,會(huì)有一長(zhǎng)串亂碼,這是二進(jìn)制文件
當(dāng)然, 我們不需要看懂, 只要電腦能識(shí)別就行了,嚯嚯~~~
第三步:在測(cè)試類使用ObjectInputStream 對(duì)象實(shí)現(xiàn)反序列化
@Testpublic void run2() throws Exception {File file = new File("E:\\file\\student.txt"); //文件位置FileInputStream fis = new FileInputStream(file);ObjectInputStream ois = new ObjectInputStream(fis);Area a = (Area) ois.readObject();System.out.println("id:" + a.getId());System.out.println("aname:" + a.getName());//關(guān)閉ois.close();fis.close();}
輸出結(jié)果:
問題1: 如果我們某些數(shù)據(jù)不需要序列化,怎么辦?
解決: 在字段面前加上 transient
Transient關(guān)鍵字的作用是控制變量的序列化,在變量聲明前加上該關(guān)鍵字,可以阻止該變量被序列化到文件中,在被反序列化后,transient變量的值被設(shè)為初始值,如int型的是0,對(duì)象型的是 null。
transient表示瞬態(tài)的,被transient關(guān)鍵字修飾的變量不再能被序列化,一個(gè)靜態(tài)變量不管是否被transient修飾,均不能被序列化。
@Idprivate Integer id; //序列化transient private String name; //不需要序列化
}
運(yùn)行結(jié)果如下:
問題2: 序列化版本問題,在完成序列化操作后,由于項(xiàng)目的升級(jí)或修改,可能我們會(huì)對(duì)序列化對(duì)象進(jìn)行修改,比如增加某個(gè)字段,那么我們?cè)谶M(jìn)行反序列化就會(huì)報(bào)錯(cuò):
解決:
在 JavaBean 對(duì)象中增加一個(gè) serialVersionUID 字段,用來(lái)固定這個(gè)版本,無(wú)論我們?cè)趺葱薷?#xff0c;版本都是一致的,就能進(jìn)行反序列化了
private static final long serialVersionUID = 8656128222714547171L;
五. 注意事項(xiàng)
1、序列化時(shí),只對(duì)對(duì)象的狀態(tài)進(jìn)行保存,而不管對(duì)象的方法;
2、當(dāng)一個(gè)父類實(shí)現(xiàn)序列化,子類自動(dòng)實(shí)現(xiàn)序列化,不需要顯式實(shí)現(xiàn)Serializable接口;
3、當(dāng)一個(gè)對(duì)象的實(shí)例變量引用其他對(duì)象,序列化該對(duì)象時(shí)也把引用對(duì)象進(jìn)行序列化;
4、并非所有的對(duì)象都可以序列化,至于為什么不可以,有很多原因了,比如:
安全方面的原因,比如一個(gè)對(duì)象擁有private,public等f(wàn)ield,對(duì)于一個(gè)要傳輸?shù)膶?duì)象,比如寫到文件,或者進(jìn)行RMI傳輸?shù)鹊?#xff0c;在序列化進(jìn)行傳輸?shù)倪^程中,這個(gè)對(duì)象的private等域是不受保護(hù)的;
資源分配方面的原因,比如socket,thread類,如果可以序列化,進(jìn)行傳輸或者保存,也無(wú)法對(duì)他們進(jìn)行重新的資源分配,而且,也是沒有必要這樣實(shí)現(xiàn);
5、聲明為static和transient類型的成員數(shù)據(jù)不能被序列化。因?yàn)閟tatic代表類的狀態(tài),transient代表對(duì)象的臨時(shí)數(shù)據(jù)。
6、序列化運(yùn)行時(shí)使用一個(gè)稱為 serialVersionUID 的版本號(hào)與每個(gè)可序列化類相關(guān)聯(lián),該序列號(hào)在反序列化過程中用于驗(yàn)證序列化對(duì)象的發(fā)送者和接收者是否為該對(duì)象加載了與序列化兼容的類。為它賦予明確的值。顯式地定義serialVersionUID有兩種用途:
在某些場(chǎng)合,希望類的不同版本對(duì)序列化兼容,因此需要確保類的不同版本具有相同的serialVersionUID;
在某些場(chǎng)合,不希望類的不同版本對(duì)序列化兼容,因此需要確保類的不同版本具有不同的serialVersionUID。
7、Java有很多基礎(chǔ)類已經(jīng)實(shí)現(xiàn)了serializable接口,比如String,Vector等。但是也有一些沒有實(shí)現(xiàn)serializable接口的;
8、如果一個(gè)對(duì)象的成員變量是一個(gè)對(duì)象,那么這個(gè)對(duì)象的數(shù)據(jù)成員也會(huì)被保存!這是能用序列化解決深拷貝的重要原因;