java序列化和反序列化以及序列化ID的作用分析
?java序列化和反序列化
一、概念
? ? ? ?java對象序列化的意思就是將對象的狀態(tài)轉(zhuǎn)化成字節(jié)流,以后可以通過這些值再生成相同狀態(tài)的對象。對象序列化是對象持久化的一種實(shí)現(xiàn)方法,它是將對象的屬性和方法轉(zhuǎn)化為一種序列化的形式用于存儲和傳輸。反序列化就是根據(jù)這些保存的信息重建對象的過程。
? ? ? ?序列化:將java對象轉(zhuǎn)化為字節(jié)序列的過程。
? ? ? ?反序列化:將字節(jié)序列轉(zhuǎn)化為java對象的過程。
二、為什么要序列化和反序列化
? ??? ?我們知道,當(dāng)兩個進(jìn)程進(jìn)行遠(yuǎn)程通信時,可以相互發(fā)送各種類型的數(shù)據(jù),包括文本、圖片、音頻、視頻等,?而這些數(shù)據(jù)都會以二進(jìn)制序列的形式在網(wǎng)絡(luò)上傳送。那么當(dāng)兩個Java進(jìn)程進(jìn)行通信時,能否實(shí)現(xiàn)進(jìn)程間的對象傳送呢?答案是可以的。如何做到呢?這就需要Java序列化與反序列化了。換句話說,一方面,發(fā)送方需要把這個Java對象轉(zhuǎn)換為字節(jié)序列,然后在網(wǎng)絡(luò)上傳送;另一方面,接收方需要從字節(jié)序列中恢復(fù)出Java對象。當(dāng)我們明晰了為什么需要Java序列化和反序列化后,我們很自然地會想Java序列化的好處。其好處一是實(shí)現(xiàn)了數(shù)據(jù)的持久化,通過序列化可以把數(shù)據(jù)永久地保存到硬盤上(通常存放在文件里),二是,利用序列化實(shí)現(xiàn)遠(yuǎn)程通信,即在網(wǎng)絡(luò)上傳送對象的字節(jié)序列。
三、涉及到的javaAPI
? ? ? ? ??java.io.ObjectOutputStream表示對象輸出流,它的writeObject(Object obj)方法可以對參數(shù)指定的obj對象進(jìn)行序列化,把得到的字節(jié)序列寫到一個目標(biāo)輸出流中。
? ? ? ? ??java.io.ObjectInputStream表示對象輸入流,它的readObject()方法源輸入流中讀取字節(jié)序列,再把它們反序列化成為一個對象,并將其返回。
? ? ? ? ?只有實(shí)現(xiàn)了Serializable或Externalizable接口的類的對象才能被序列化,否則拋出異常。
四、序列化和反序列化的步驟
? ? ? ? ?序列化:
? ? ? ? ? ?步驟一:創(chuàng)建一個對象輸出流,它可以包裝一個其它類型的目標(biāo)輸出流,如文件輸出流:
? ? ? ? ? ? ? ? ? ? ? ? ? ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(“目標(biāo)地址路徑”));
? ? ? ? ?步驟二:通過對象輸出流的writeObject()方法寫對象:
? ? ? ? ? ? ? ? ? ? ? ? ? out.writeObject("Hello");
? ? ? ? ? ? ? ? ? ? ? ? ? out.writeObject(new Date());
? ? ? ? ?反序列化: ? ?? ??
? ? ? ? ??步驟一:創(chuàng)建一個對象輸入流,它可以包裝一個其它類型輸入流,如文件輸入流:
? ? ? ? ? ? ? ? ? ? ? ? ? ObjectInputStream in = new ObjectInputStream(new fileInputStream(“目標(biāo)地址路徑”));
? ? ? ? ?步驟二:通過對象輸出流的readObject()方法讀取對象:
? ? ? ? ? ? ? ? ? ? ? ? String obj1 = (String)in.readObject();
? ? ? ? ? ? ? ? ? ? ? ? Date obj2 = ?(Date)in.readObject();
? ? ? ? 說明:為了正確讀取數(shù)據(jù),完成反序列化,必須保證向?qū)ο筝敵隽鲗憣ο蟮捻樞蚺c從對象輸入流中讀對象的順序一致。
五、舉個例子
? ? ?? 我們首先寫個Person實(shí)現(xiàn)Serializable接口:
[java]?view plaincopy print?
? ? ? ??
? ? ? ? ?其次,我們在main()里面寫個方法,執(zhí)行序列化過程:
[java]?view plaincopy print?
? ? ? ??
? ? ? ? 我們查看一下hello.txt文件中的內(nèi)容,里面是一串字節(jié)序列,打開該文件的時候不要用自帶的記事本打開,因?yàn)樯婕暗阶址幋a的問題,所以顯示的話是一串亂碼,建議用SublimeText打開。
? ? ? ???
? ? ? ? 我們再寫個方法來反序列化該字節(jié)成Person對象,并打印出里面的值。
[java]?view plaincopy print?
? ? ? ? ?
? ? ? ? 執(zhí)行反序列化的代碼也是很簡單的,首先創(chuàng)建一個輸入流對象ObjectInputStream,然后從指定的目錄下“E:/hello.txt”獲取它的字節(jié)序列,然后通過輸入流對象的readObject()方法將其獲得的對象強(qiáng)制轉(zhuǎn)化為Person對象,這就完成了反序列化工作,正確的反序列化成功的情況下控制臺打印輸出為:
Java?序列化ID的作用
? ? ? ?有關(guān)序列化和反序列化的概念,可以查看前一篇《java序列化和反序列化使用總結(jié)》的講解,這一篇主要說明一下序列化過程中出現(xiàn)的問題即java序列化和反序列化中ID的作用。
? ? ? ? 在前一篇的介紹中,我們在代碼里會發(fā)現(xiàn)有這樣一個變量:serialVersionUID,那么這個變量serialVersionUID到底具有什么作用呢?能不能去掉呢?
[java]?view plaincopy print?
? ? ? ?序列化ID的作用:??
? ? ? ?其實(shí),這個序列化ID起著關(guān)鍵的作用,它決定著是否能夠成功反序列化!簡單來說,java的序列化機(jī)制是通過在運(yùn)行時判斷類的serialVersionUID來驗(yàn)證版本一致性的。在進(jìn)行反序列化時,JVM會把傳來的字節(jié)流中的serialVersionUID與本地實(shí)體類中的serialVersionUID進(jìn)行比較,如果相同則認(rèn)為是一致的,便可以進(jìn)行反序列化,否則就會報序列化版本不一致的異常。等會我們可以通過代碼驗(yàn)證一下。
? ? ? ?序列化ID如何產(chǎn)生:
? ? ? ?當(dāng)我們一個實(shí)體類中沒有顯示的定義一個名為“serialVersionUID”、類型為long的變量時,Java序列化機(jī)制會根據(jù)編譯時的class自動生成一個serialVersionUID作為序列化版本比較,這種情況下,只有同一次編譯生成的class才會生成相同的serialVersionUID。譬如,當(dāng)我們編寫一個類時,隨著時間的推移,我們因?yàn)樾枨蟾膭?#xff0c;需要在本地類中添加其他的字段,這個時候再反序列化時便會出現(xiàn)serialVersionUID不一致,導(dǎo)致反序列化失敗。那么如何解決呢?便是在本地類中添加一個“serialVersionUID”變量,值保持不變,便可以進(jìn)行序列化和反序列化。
? ? ? ?驗(yàn)證“serialVersionUID”不一致導(dǎo)致反序列化失敗
[java]?view plaincopy print?
[java]?view plaincopy print?
? ? ? ? 運(yùn)行一下,會在控制臺中打印“序列化成功。”,然后我們在Person類中再添加一個字段,name,然后直接從E:/hello.txt中反序列化,再運(yùn)行一下,看看會出現(xiàn)什么問題。
[java]?view plaincopy print?
? ? ? ?運(yùn)行一下,不出意外,報了一個異常。
? ? ??
? ? ? 從上面兩張圖便可以看出兩次的序列化ID是不一樣的,導(dǎo)致反序列化失敗。
總結(jié):
? ? ? ?虛擬機(jī)是否允許反序列化,不僅取決于類路徑和功能代碼是否一致,一個非常重要的一點(diǎn)是兩個類的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L)。
總結(jié)
以上是生活随笔為你收集整理的java序列化和反序列化以及序列化ID的作用分析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java开发微信公众平台(一)-- 服务
- 下一篇: hibernate三种状态:临时状态、游