1. 程式人生 > >【轉】什麼是序列化,為什麼要序列化

【轉】什麼是序列化,為什麼要序列化

原文:http://blog.163.com/benbenfafa_88/blog/static/64930162201152373158142/

簡單來說序列化就是一種用來處理物件流的機制,所謂物件流也就是將物件的內容進行流化,流的概念這裡不用多說(就是I/O),我們可以對流化後的物件進行讀寫操作,也可將流化後的物件傳輸於網路之間(注:要想將物件傳輸於網路必須進行流化)!在對物件流進行讀寫操作時會引發一些問題,而序列化機制正是用來解決這些問題的!
問題的引出:
如上所述,讀寫物件會有什麼問題呢?比如:我要將物件寫入一個磁碟檔案而後再將其讀出來會有什麼問題嗎?別急,其中一個最大的問題就是物件引用!舉個例子來說:假如我有兩個類,分別是A和B,B類中含有一個指向A類物件的引用,現在我們對兩個類進行例項化{ A a = new A(); B b = new B(); },這時在記憶體中實際上分配了兩個空間,一個儲存物件a,一個儲存物件b,接下來我們想將它們寫入到磁碟的一個檔案中去,就在寫入檔案時出現了問題!因為物件b包含對物件a的引用,所以系統會自動的將a的資料複製一份到b中,這樣的話當我們從檔案中恢復物件時(也就是重新載入到記憶體中)時,記憶體分配了三個空間,而物件a同時在記憶體中存在兩份,想一想後果吧,如果我想修改物件a的資料的話,那不是還要搜尋它的每一份拷貝來達到物件資料的一致性,這不是我們所希望的!


以下序列化機制的解決方案:
1.儲存到磁碟的所有物件都獲得一個序列號(1, 2, 3等等)
2.當要儲存一個物件時,先檢查該物件是否被儲存了。
3.如果以前儲存過,只需寫入"與已經儲存的具有序列號x的物件相同"的標記,否則,儲存該物件
通過以上的步驟序列化機制解決了物件引用的問題!
序列化的實現
將需要被序列化的類實現Serializable介面,該介面沒有需要實現的方法,implements Serializable只是為了標註該物件是可被序列化的,然後使用一個輸出流(如:FileOutputStream)來構造一個 ObjectOutputStream(物件流)物件,接著,使用ObjectOutputStream物件的writeObject(Object obj)方法就可以將引數為obj的物件寫出(即儲存其狀態),要恢復的話則用輸入流。

在序列化的過程中,有些資料欄位我們不想將其序列化,對於此類欄位我們只需要在定義時給它加上transient關鍵字即可,對於transient欄位序列化機制會跳過不會將其寫入檔案,當然也不可被恢復。但有時我們想將某一欄位序列化,但它在SDK中的定義卻是不可序列化的型別,這樣的話我們也必須把他標註為transient,可是不能寫入又怎麼恢復呢?好在序列化機制為包含這種特殊問題的類提供瞭如下的方法定義:
private void readObject(ObjectInputStream in) throws
IOException, ClassNotFoundException;
private void writeObject(ObjectOutputStream out) throws

IOException;
(注:這些方法定義時必須是私有的,因為不需要你顯示呼叫,序列化機制會自動呼叫的)
使用以上方法我們可以手動對那些你又想序列化又不可以被序列化的資料欄位進行寫出和讀入操作。
下面是一個典型的例子,java.awt.geom包中的Point2D.Double類就是不可序列化的,因為該類沒有實現Serializable介面,在我的例子中將把它當作LabeledPoint類中的一個數據欄位,並演示如何將其序列化!
import java.io.*;
import java.awt.geom.*;
public class TransientTest
{
 public static void main(String[] args)
 {
 LabeledPoint label = new LabeledPoint("Book", 5.00, 5.00);
 try
 {
 System.out.println(label);// 寫入前
 ObjectOutputStream out = new ObjectOutputStream(new
 FileOutputStream("Label.txt"));
 out.writeObject(label);
 out.close();
 System.out.println(label);// 寫入後
 ObjectInputStream in = new ObjectInputStream(new