當兩個進程在進行遠程通信時彼此可以發送各種類型的數據無論是何種類型的數據都會以二進制序列的形式在網絡上傳送發送方需要把這個Java對象轉換為字節序列才能在網絡上傳送接收方則需要把字節序列再恢復為Java對象
把Java對象轉換為字節序列的過程稱為對象的序列化
把字節序列恢復為Java對象的過程稱為對象的反序列化
對象的序列化主要有兩種用途
) 把對象的字節序列永久地保存到硬盤上通常存放在一個文件中
) 在網絡上傳送對象的字節序列
一JDK類庫中的序列化API
javaioObjectOutputStream代表對象輸出流它的writeObject(Object obj)方法可對參數指定的obj對象進行序列化把得到的字節序列寫到一個目標輸出流中
javaioObjectInputStream代表對象輸入流它的readObject()方法從一個源輸入流中讀取字節序列再把它們反序列化為一個對象並將其返回
只有實現了Serializable和Externalizable接口的類的對象才能被序列化Externalizable接口繼承自Serializable接口實現Externalizable接口的類完全由自身來控制序列化的行為而僅實現Serializable接口的類可以采用默認的序列化方式
對象序列化包括如下步驟
) 創建一個對象輸出流它可以包裝一個其他類型的目標輸出流如文件輸出流
) 通過對象輸出流的writeObject()方法寫對象
對象反序列化的步驟如下
) 創建一個對象輸入流它可以包裝一個其他類型的源輸入流如文件輸入流
) 通過對象輸入流的readObject()方法讀取對象
下面讓我們來看一個對應的例子類的內容如下
import javaio*
import javautilDate
/**
* 對象的序列化和反序列化測試類
* @author < a >xie>AmigoXie< /a>
* @version
* Creation date 下午
*/
public class ObjectSaver {
/**
* @param args
* @author < a >xie>AmigoXie< /a>
* Creation date 下午
*/
public static void main(String[] args) throws Exception {
ObjectOutputStream out = new ObjectOutputStream
(new FileOutputStream(DobjectFileobj))
//序列化對象
Customer customer = new Customer(阿蜜果 )
outwriteObject(你好!)
outwriteObject(new Date())
outwriteObject(customer)
outwriteInt() //寫入基本類型數據
outclose()
//反序列化對象
ObjectInputStream in = new ObjectInputStream
(new FileInputStream(DobjectFileobj))
Systemoutprintln(obj= + (String) inreadObject())
Systemoutprintln(obj= + (Date) inreadObject())
Customer obj = (Customer) inreadObject()
Systemoutprintln(obj= + obj)
int obj = inreadInt()
Systemoutprintln(obj= + obj)
inclose()
}
}
class Customer implements Serializable {
private String name
private int age
public Customer(String name int age) {
thisname = name
thisage = age
}
public String toString() {
return name= + name + age= + age
}
}
輸出結果如下
obj=你好!
obj=Sat Sep CST
obj=name=阿蜜果 age=
obj=
因此例比較簡單在此不再詳述
二實現Serializable接口
ObjectOutputStream只能對Serializable接口的類的對象進行序列化默認情況下ObjectOutputStream按照默認方式序列化這種序列化方式僅僅對對象的非transient的實例變量進行序列化而不會序列化對象的transient的實例變量也不會序列化靜態變量
當ObjectOutputStream按照默認方式反序列化時具有如下特點
) 如果在內存中對象所屬的類還沒有被加載那麼會先加載並初始化這個類如果在classpath中不存在相應的類文件那麼會拋出ClassNotFoundException
) 在反序列化時不會調用類的任何構造方法
如果用戶希望控制類的序列化方式可以在可序列化類中提供以下形式的writeObject()和readObject()方法
private void writeObject(javaioObjectOutputStream out) throws IOException
private void readObject(javaioObjectInputStream in) throws IOException ClassNotFoundException
當ObjectOutputStream對一個Customer對象進行序列化時如果該對象具有writeObject()方法那麼就會執行這一方法否則就按默認方式序列化在該對象的writeObjectt()方法中可以先調用ObjectOutputStream的defaultWriteObject()方法使得對象輸出流先執行默認的序列化操作同理可得出反序列化的情況不過這次是defaultReadObject()方法
有些對象中包含一些敏感信息這些信息不宜對外公開如果按照默認方式對它們序列化那麼它們的序列化數據在網絡上傳輸時可能會被不法份子竊取對於這類信息可以對它們進行加密後再序列化在反序列化時則需要解密再恢復為原來的信息
默認的序列化方式會序列化整個對象圖這需要遞歸遍歷對象圖如果對象圖很復雜遞歸遍歷操作需要消耗很多的空間和時間它的內部數據結構為雙向列表
在應用時如果對某些成員變量都改為transient類型將節省空間和時間提高序列化的性能
三 實現Externalizable接口
Externalizable接口繼承自Serializable接口如果一個類實現了Externalizable接口那麼將完全由這個類控制自身的序列化行為Externalizable接口聲明了兩個方法
public void writeExternal(ObjectOutput out) throws IOException
public void readExternal(ObjectInput in) throws IOException ClassNotFoundException
前者負責序列化操作後者負責反序列化操作
在對實現了Externalizable接口的類的對象進行反序列化時會先調用類的不帶參數的構造方法這是有別於默認反序列方式的如果把類的不帶參數的構造方法刪除或者把該構造方法的訪問權限設置為private默認或protected級別會拋出javaioInvalidException no valid constructor異常
四可序列化類的不同版本的序列化兼容性
凡是實現Serializable接口的類都有一個表示序列化版本標識符的靜態變量
private static final long serialVersionUID
以上serialVersionUID的取值是Java運行時環境根據類的內部細節自動生成的如果對類的源代碼作了修改再重新編譯新生成的類文件的serialVersionUID的取值有可能也會發生變化
類的serialVersionUID的默認值完全依賴於Java編譯器的實現對於同一個類用不同的Java編譯器編譯有可能會導致不同的serialVersionUID也有可能相同為了提高哦啊serialVersionUID的獨立性和確定性強烈建議在一個可序列化類中顯示的定義serialVersionUID為它賦予明確的值顯式地定義serialVersionUID有兩種用途
) 在某些場合希望類的不同版本對序列化兼容因此需要確保類的不同版本具有相同的serialVersionUID
) 在某些場合不希望類的不同版本對序列化兼容因此需要確保類的不同版本具有不同的serialVersionUID
From:http://tw.wingwit.com/Article/program/Java/hx/201311/25983.html