淺析關于java的序列化和反序列化
對于序列化和反序列化,大家或多或少都會聽過一點。
所謂序列化,就是把要傳輸?shù)膶ο笠约跋嚓P信息轉換成字節(jié)數(shù)組進行存儲的過程。
而反序列化就是將字節(jié)數(shù)組再轉回對象的過程。
對于序列化和反序列化總結了幾點需要注意的地方,
1、實現(xiàn)Serializable接口的類才能夠序列化,如果是父類實現(xiàn)了該接口,子類也可以進行序列化
這點不過多解釋,規(guī)定就是這樣。
2、靜態(tài)成員不能被序列化、方法不能被序列化,關鍵字transient修飾的屬性不能序列化
話不多說,上代碼。
import java.io.*; /** * 測試靜態(tài)成員、方法、transient修飾的屬性不能序列化 */ public class SerializeTest { public static void main(String[] args) throws IOException { Test test = new Test(); // 創(chuàng)建被序列化的對象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.data")); oos.writeObject(test); oos.close(); //這里記得序列化完成之后要關流,和io流一樣,不多解釋 } } class Test implements Serializable{ //創(chuàng)建一個類用于創(chuàng)建序列化對象 static int a; transient private int b; private int c; public void m(){} }
以上代碼就是創(chuàng)建了一個類,然后對該類的對象進行序列化,序列化后的文件為test.data.然后查看了一個這個文件的大小為53字節(jié)。
下面我將這個類中的靜態(tài)成員、transient修飾的成員和m()方法都注釋掉再進行一次序列化
import java.io.*; /** * 測試靜態(tài)成員、方法、transient修飾的屬性不能序列化 */ public class SerializeTest { public static void main(String[] args) throws IOException { Test test = new Test(); // 創(chuàng)建被序列化的對象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test.data")); oos.writeObject(test); oos.close(); //這里記得序列化完成之后要關流,和io流一樣,不多解釋 } } class Test implements Serializable{ // 將static屬性、transient屬性和方法都注釋掉之后在進行一次序列化 // static int a; // transient private int b; private int c; // public void m(){} }
此時,看到再序列化之后的文件大小依然還是53字節(jié),但是如果再將成員變量c注釋掉再進行序列化就會發(fā)現(xiàn),這個文件變小了
由此可以說明,靜態(tài)成員、transient修飾的成員和方法都不能序列化。
3、對于向上造型的對象進行序列化,實際序列化的對象是實際創(chuàng)建類
上代碼
import java.io.*; /** * 測試向上造型的對象實際序列化的是實際創(chuàng)建類 */ public class SerializeTest { public static void main(String[] args) throws IOException, ClassNotFoundException { Test test = new Test02(); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("test02.data")); oos.writeObject(test); oos.close(); ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test02.data")); System.out.println(ois.readObject()); //這里直接打印反序列化之后的對象信息 ois.close(); } } class Test implements Serializable{ //創(chuàng)建一個父類類用于創(chuàng)建序列化對象 } class Test02 extends Test{ // 子類繼承父類 }
上面代碼的執(zhí)行結果:Test02@4f3f5b24,可以看到打印的就是子類的對象,so,這個就證明了向上造型的對象,實際序列化的是實際創(chuàng)建類,同時也證明了如果父類實現(xiàn)了Serializable接口,子類也是可以序列化滴。
4、對于已經序列化完的對象,在進行反序列化時,如果對原類信息進行了修改(包含不能被序列化的屬性和方法),此時將不能進行反序列化
直接上代碼。
import java.io.*; /** * 測試反序列化之前如果修改原類信息將不能進行反序列化 * */ public class SerializeTest { public static void main(String[] args) throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream("test02.data")); System.out.println(ois.readObject()); //這里直接打印反序列化之后的對象信息 ois.close(); } } class Test implements Serializable{ //創(chuàng)建一個父類類用于創(chuàng)建序列化對象 } class Test02 extends Test{ // 子類繼承父類 // test02.data這個文件就是第三條中創(chuàng)建對象序列化之后的文件 static int a = 1; //這里我隨便添加了一個靜態(tài)屬性,然后執(zhí)行代碼 }
///下面是執(zhí)行結果
Exception in thread "main" java.io.InvalidClassException: night.Test02; local class incompatible: stream classdesc serialVersionUID = -4461189770521473347, local class serialVersionUID = -7303293535763263875
at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:616)
at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1829)
at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1713)
at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1986)
at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1535)
at java.io.ObjectInputStream.readObject(ObjectInputStream.java:422)
at night.SerializeTest.main(SerializeTest.java:23)
居然報了異常?
這里為什么會報異常呢,簡單講一哈:序列化的時候會計算一個序列化版本號-----serialVersionUID,并且將這個版本號一同存放到文件中,在反序列化的時候會獲取此序列號,并且會計算當前類的serialVersionUID,如果兩個版本號不相等就會報如上異常,反之反序列化就可以成功。~
那么對于這種情況就沒有解決辦法了? 當然是有的。
我們可以在類中直接聲明serialVersionUID為 final屬性,并讓其自動賦初始值(這個值jvm肯定是計算過的了),格式如下:
private static final long serialVersionUID = 362498820763181265L;
這樣不管怎樣修改類的屬性,這個serialVersionUID都不會改變,這樣問題就解決了。
其實對于private static final long serialVersionUID = 362498820763181265L;這個東西,大家應該都見過的,比如我這里拿的就是HashMap類源碼中的那個值。
以上就是淺析關于java的序列化和反序列化的詳細內容,更多關于java序列化和反序列化的資料請關注腳本之家其它相關文章!
相關文章
java自定義注解實現(xiàn)前后臺參數(shù)校驗的實例
下面小編就為大家?guī)硪黄猨ava自定義注解實現(xiàn)前后臺參數(shù)校驗的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-11-11Reactor3 Map與FlatMap的區(qū)別示例詳解
這篇文章主要為大家介紹了Reactor3 Map與FlatMap的區(qū)別示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-08-08java創(chuàng)建一個類實現(xiàn)讀取一個文件中的每一行顯示出來
下面小編就為大家?guī)硪黄猨ava創(chuàng)建一個類實現(xiàn)讀取一個文件中的每一行顯示出來的實例。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-01-01使用logback實現(xiàn)按自己的需求打印日志到自定義的文件里
這篇文章主要介紹了使用logback實現(xiàn)按自己的需求打印日志到自定義的文件里,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-08-08Java 的 FileFilter文件過濾與readline讀行操作實例代碼
這篇文章介紹了Java 的 FileFilter文件過濾與readline讀行操作實例代碼,有需要的朋友可以參考一下2013-09-09SpringBoot整合RabbitMQ實戰(zhàn)教程附死信交換機
這篇文章主要介紹了SpringBoot整合RabbitMQ實戰(zhàn)附加死信交換機,本文通過示例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-06-06在MyBatis中實現(xiàn)一對多查詢和多對一查詢的方式詳解(各兩種方式)
今天通過兩種方法分別給大家介紹在MyBatis中實現(xiàn)一對多查詢和多對一查詢的方式,每種方式通過實例代碼給大家介紹的非常詳細,需要的朋友參考下吧2022-01-01