1)什麼是對象序列化和反序列化
把對象轉換爲 字節序列的過程稱爲 對象的序列化。
把字節序列 恢復爲對象的過程稱爲 對象的反序列化。
對象的序列化主要有兩種用途:
1) 把對象的字節序列 永久地保存到硬盤上,通常存放在一個文件中;
2) 在網絡上 傳送對象的字節序列。
2)具體使用場景
在很多應用中,需要對某些對象進行序列化,讓它們離開內存空間,入住物理硬盤,以便長期保存。
比如:
一、最常見的是Web服務器中的Session對象,當有 10萬用戶併發訪問,就有可能出現10萬個Session對象,內存可能喫不消,
於是Web容器就會把一些seesion先序列化到硬盤中,等要用了,再把保存在硬盤中的對象還原到內存中。
二、 RPC應用,比如 Dubbo hession等
當兩個進程在進行遠程通信時,彼此可以發送各種類型的數據。無論是何種類型的數據,都會以二進制序列的形式在網絡上傳送。 發送方需要把這個Java對象轉換爲字節序列,才能在網絡上傳送;接收方則需要把字節序列再恢復爲Java對象。
3)怎麼使用 (JDK類庫中的序列化API)
只有實現了Serializable和Externalizable接口的類的對象才能被序列化。
Externalizable接口繼承自 Serializable接口, 實現Externalizable接口的類完全由自身來控制序列化的行爲,可以指定序列化哪些屬性。
實現Serializable接口的類可以 採用默認的序列化方式 。
對象序列化使用 ObjectOutputStream ,具體使用步驟:
1) 創建一個對象輸出流,它可以包裝一個其他類型的目標輸出流,如文件輸出流;
2) 通過對象輸出流的writeObject(Object obj)方法寫到一個目標輸出流中。
對象反序列化使用ObjectInputStream ,具體使用步驟:
1) 創建一個對象輸入流,它可以包裝一個其他類型的源輸入流,如文件輸入流;
2) 通過對象輸入流的readObject()方法讀取對象。
3)注意點
實現Serializable接口的類,經常會出現警告提示:
Eclipse就會有這個提示:The serializable class User does not declare a static final serialVersionUID field of type long,意思就是說讓你添加一個serialVersionUID的值。
使用補全功能就會添加一行代碼 private static final long serialVersionUID = 1L;
serialVersionUID他有什麼作用呢?
serialVersionUID: 字面意思上是序列化的版本號,凡是實現Serializable接口的類都有一個表示序列化版本標識符的靜態變量
Java的序列化機制是通過判斷類的serialVersionUID來驗證版本一致性的。
在進行反序列化時,JVM會把傳來的字節流中的serialVersionUID與本地相應實體類的serialVersionUID進行比較,
如果相同就認爲是一致的,可以進行反序列化, 否則就會出現序列化版本不一致的異常,即是Invalid Cast Exception。
具體實例?比如我們寫rpc程序,遠端和本地都有一個類user,類都實現了Serializable,可以被序列化。遠端有一個同樣類,有一天遠端要在類上打印一下日誌,加了一行代碼,遠端程序反序列化的時候就會報 Invalid Cast Exception 。如果這兩個類都寫了serialVersionUID而且值一致就可以正常反序列化。如果不寫serialVersionUID,編譯器會根據這個類的結構(成員變量,成員變量的個數等),生成一個hash值,然後將這個值作爲serialVersionUID。所以類的結構有變化hash值就不一樣,就會出現報錯的現象。
虛擬機是否允許反序列化,取決於類路徑和功能代碼是否一致,兩個類的序列化 ID 是否一致(就是 private static final long serialVersionUID = 1L)
除此之外還有幾點要注意:
1 序列化保存的是對象的狀態,靜態變量屬於類的狀態,序列化並不保存靜態變量
2 Transient 關鍵字的作用是控制變量的序列化,在變量聲明前加上該關鍵字,
可以阻止該變量被序列化到文件中,在被反序列化後,transient 變量的值被設爲初始值,如 int 型的是 0,對象型的是 null
3 Java 序列化機制爲了節省磁盤空間,具有特定的存儲規則,當寫入文件的爲同一對象時,
並不會再將對象的內容進行存儲,而只是再次存儲一份引用