Bean Serializable Interface 的介面讓BEAN可以序列化,將其變成一個可儲存為以後使用的二進位流。當一個BEAN被系列化到磁碟上或者其他任何地方,其狀態被儲存起來,其中的屬性值也不會改變。在BEAN的規範中,JSP並沒有要求BEAN實現Serializable介面。但是,如果您希望自己控制您所建立的組件的serialization進程,或者您想serialize並不是標準組件擴充的組件,您必須瞭解serialization and deserialization的細節。
有幾個原因你會把BEAN冷藏起來以備後用。有些伺服器通過將所有的SESSION 資料(包括BEAN)寫入磁碟來支援任意長的SESSION生命期,即使伺服器停機也不會丟失。當伺服器重新啟動後,序列化的資料被恢複。同樣的理由,在重負載的網站上支援伺服器分簇的環境中,許多伺服器通過序列化來複製SESSION。如果你的BEAN不支援序列化,伺服器就不能正確地儲存和傳輸類。
通過同樣的策略,你可以選擇將BEAN儲存在磁碟上或者資料庫中,以備後用。例如,也許可以將客戶的購物車實現為一個BEAN,在訪問期間將其儲存在資料庫中。
如果BEAN需要特殊的複雜的初始設定,可以將BEAN設定好後序列化儲存在磁碟上。這個BEAN的“快照”可以用在任何需要的地方,包括在$#@60;jsp:useBean$#@62;中用beanName屬性的調用。
$#@60;jsp:useBean$#@62;標籤中的beanName屬性,用來執行個體化一個序列化的BEAN,而不是用來從一個類建立一個全新的執行個體。如果BEAN還沒有建立,beanName屬性傳給java.beans.Bean.instantiate()方法,由類裝載器對類進行執行個體化。它首先假定存在一個序列化的BEAN(帶有副檔名.ser),然後會將其啟用。如果這個操作失敗,它就會執行個體化一個新的執行個體。
下面簡單介紹一下這個介面:
對象能包含其它的對象,而這其它的對象又可以包含另外的對象。JAVA serialization能夠自動的處理嵌套的對象。對於一個對象的簡單的域,writeObject()直接將值寫入流。而,當遇到一個對象域時,writeObject()被再次調用,如果這個對象內嵌另一個對象,那麼,writeObject() 又被調用,直到對象能被直接寫入流為止。程式員所需要做的是將對象傳入ObjectOutputStream 的writeObject() 方法,剩下的將又系統自動完成。下面的例子建立了一個調用mine對象的PersonalData對象。代碼實現的是將一個串和mine 對象輸出到一個流,並存入一個檔案:
public class PersonalData implements Serializable {
public int id
public int yearOfBirth;
public float yearlySalary;
}
PersonalData mine = new PersonalData(101, 1956, 46500.00);
FileOutputStream outstream = new FileOutputStream("PersonalData.ser");
ObjectOutputStream out = new ObjectOutputStream(outstream);
out.writeObject("My personal data"); //將一個串寫入流
out.writeObject(mine); //將這個對象寫入流
out.close(); // 清空並關閉流
...
一個FileOutputStream對象被建立且傳到一個ObjectOutputStream。當out.writeObject() 被調用,這個串和mine 對象被objects are serializ順序加入一個存入檔案PersonalData.ser的位元組對列。
您應該注意上述類是實現的java.io.Serializable介面。因為它並未指定要實現的方法,所以Serializable被稱為"tagging interface" ,但是它僅僅"tags"它自己的對象是一個特殊的類型。任一個您希望serialize的對象都應該實現這個介面。這是必須的。否則,用到流技術時將根本不工作。例如,如果您試著去serialize 一個沒有實現這個介面的對象,一個 NotSerializableException將產生。
類通過實現 java.io.Serializable 介面以啟用其序列化功能。未實現此介面的類將無法使其任何狀態序列化或還原序列化。可序列化類別的所有子類型本身都是可序列化的。序列化介面沒有方法或欄位,僅用於標識可序列化的語義。
Java的"對象序列化"能讓你將一個實現了Serializable介面的對象轉換成一組byte,這樣日後要用這個對象時候,你就能把這些byte資料恢複出來,並據此重新構建那個對象了。
要想序列化對象,你必須先建立一個OutputStream,然後把它嵌進ObjectOutputStream。這時,你就能用writeObject( )方法把對象寫入OutputStream了。
writeObject 方法負責寫入特定類的對象的狀態,以便相應的 readObject 方法可以還原它。通過調用 out.defaultWriteObject 可以調用儲存 Object 的欄位的預設機制。該方法本身不需要涉及屬於其超類或子類的狀態。狀態是通過使用 writeObject 方法或使用 DataOutput 支援的用於基礎資料型別 (Elementary Data Type)的方法將各個欄位寫入 ObjectOutputStream 來儲存的。
讀的時候,你得把InputStream嵌到ObjectInputStream裡面,然後再調用readObject( )方法。不過這樣讀出來的,只是一個Object的reference,因此在用之前,還得先下傳。readObject 方法負責從流中讀取並還原類欄位。它可以調用 in.defaultReadObject 來調用預設機制,以還原對象的非靜態和非瞬態欄位。
defaultReadObject 方法使用流中的資訊來分配流中通過當前對象中相應命名欄位儲存的對象的欄位。這用於處理類發展後需要添加新欄位的情形。該方法本身不需要涉及屬於其超類或子類的狀態。狀態是通過使用 writeObject 方法或使用 DataOutput 支援的用於基礎資料型別 (Elementary Data Type)的方法將各個欄位寫入 ObjectOutputStream 來儲存的。
看一個列子:
import java.io. * ;
class tree implements java.io.Serializable {
public tree left;
public tree right;
public int id;
public int level;
private static int count = 0 ;
public tree( int depth) {
id = count ++ ;
level = depth;
if (depth > 0 ) {
left = new tree(depth - 1 );
right = new tree(depth - 1 );
}
}
public void print( int levels) {
for ( int i = 0 ; i < level; i ++ )
System.out.print( " " );
System.out.println( " node " + id);
if (level <= levels && left != null )
left.print(levels);
if (level <= levels && right != null )
right.print(levels);
}
public static void main (String argv[]) {
try {
/**/ /* 建立一個檔案寫入序列化樹。 */
FileOutputStream ostream = new FileOutputStream( " tree.tmp " );
/**/ /* 建立輸出資料流 */
ObjectOutputStream p = new ObjectOutputStream(ostream);
/**/ /* 建立一個二層的樹。 */
tree base = new tree( 2 );
p.writeObject(base); // 將樹寫入流中。
p.writeObject( " LiLy is 惠止南國 " );
p.flush();
ostream.close(); // 關閉檔案。