Serializable java序列化
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 支持的用于基本數據類型的方法將各個字段寫入 ObjectOutputStream 來保存的。
讀的時候,你得把InputStream嵌到ObjectInputStream里面,然后再調用readObject( )方法。不過這樣讀出來的,只是一個Object的reference,因此在用之前,還得先下傳。readObject 方法負責從流中讀取并還原類字段。它可以調用 in.defaultReadObject 來調用默認機制,以還原對象的非靜態和非瞬態字段。
defaultReadObject 方法使用流中的信息來分配流中通過當前對象中相應命名字段保存的對象的字段。這用于處理類發展后需要添加新字段的情形。該方法本身不需要涉及屬于其超類或子類的狀態。狀態是通過使用 writeObject 方法或使用 DataOutput 支持的用于基本數據類型的方法將各個字段寫入 ObjectOutputStream 來保存的。
看一個列子:
?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();?????//??關閉文件。??
?
??????????????/*??打開文件并設置成從中讀取對象。??*/??
????????????FileInputStream?istream??=???new??FileInputStream(?"?tree.tmp?"?);?
????????????ObjectInputStream?q??=???new??ObjectInputStream(istream);?
?????????????/*??讀取樹對象,以及所有子樹??*/??
????????????tree?new_tree??=??(tree)q.readObject();?
????????????new_tree.print(?2?);???//??打印出樹形結構的最上面?2級??
?????????????String?name??=??(String)q.readObject();
????????????System.out.println(?"?/n?"?+?name);
????????}???catch??(Exception?ex)??{?
????????????ex.printStackTrace();?
????????}??
????}??
}??
?
最后結果如下:
??? node 0
? node 1
node 2
node 3
? node 4
node 5
node 6
LiLy is 惠止南國?
可以看到,在序列化的時候,writeObject與readObject之間的先后順序。readObject將最先write的object read出來。用數據結構的術語來講就姑且稱之為先進先出吧!
在序列化時,有幾點要注意的:
1:當一個對象被序列化時,只保存對象的非靜態成員變量,不能保存任何的成員方法和靜態的成員變量。
2:如果一個對象的成員變量是一個對象,那么這個對象的數據成員也會被保存。
3:如果一個可序列化的對象包含對某個不可序列化的對象的引用,那么整個序列化操作將會失敗,并且會拋出一個NotSerializableException。我們可以將這個引用標記為transient,那么對象仍然可以序列化
還有我們對某個對象進行序列化時候,往往對整個對象全部序列化了,比如說類里有些數據比較敏感,不希望序列化,一個方法可以用transient來標識,另一個方法我們可以在類里重寫
??????throws??IOException,?ClassNotFoundException;
??private???void??writeObject(java.io.ObjectOutputStream?stream)
??????throws??IOException
這二個方法!
示例:
?class??ObjectSerialTest
?{
?????public???static???void??main(String[]?args)??throws??Exception
?????{
????????Employee?e1?=?new??Employee(?"?zhangsan?"?,?25?,?3000.50?);
????????Employee?e2?=?new??Employee(?"?lisi?"?,?24?,?3200.40?);
????????Employee?e3?=?new??Employee(?"?wangwu?"?,?27?,?3800.55?);
????????
????????FileOutputStream?fos?=?new??FileOutputStream(?"?employee.txt?"?);
????????ObjectOutputStream?oos?=?new??ObjectOutputStream(fos);
????????oos.writeObject(e1);
????????oos.writeObject(e2);
????????oos.writeObject(e3);
????????oos.close();
????????
????????FileInputStream?fis?=?new??FileInputStream(?"?employee.txt?"?);
????????ObjectInputStream?ois?=?new??ObjectInputStream(fis);
????????Employee?e;
?????????for?(?int??i?=?0?;i?<?3?;i?++?)
?????????{
????????????e?=?(Employee)ois.readObject();
????????????System.out.println(e.name?+?"?:?"?+?e.age?+?"?:?"?+?e.salary);
????????}?
????????ois.close();
????}?
}?
?
?class??Employee??implements??Serializable
?{
????String?name;
?????int??age;
?????double??salary;
?????transient??Thread?t?=?new??Thread();
?????public??Employee(String?name,?int??age,?double??salary)
?????{
?????????this?.name?=?name;
?????????this?.age?=?age;
?????????this?.salary?=?salary;
????}?
?????private???void??writeObject(java.io.ObjectOutputStream?oos)??throws??IOException
?????{
????????oos.writeInt(age);
????????oos.writeUTF(name);
????????System.out.println(?"?Write?Object?"?);
????}?
?????private???void??readObject(java.io.ObjectInputStream?ois)??throws??IOException
?????{
????????age?=?ois.readInt();
????????name?=?ois.readUTF();
????????System.out.println(?"?Read?Object?"?);
????}?
?
}
--(add on 2006/6/28)?
?
參考資料:JDK1.5 API DOC? 孫鑫老師資料??
1、實現Serializable回導致發布的API難以更改,并且使得package-private和private
這兩個本來封裝的較好的咚咚也不能得到保障了
2、Serializable會為每個類生成一個序列號,生成依據是類名、類實現的接口名、
public和protected方法,所以只要你一不小心改了一個已經publish的API,并且沒有自
己定義一個long類型的叫做serialVersionUID的field,哪怕只是添加一個getXX,就會
讓你讀原來的序列化到文件中的東西讀不出來(不知道為什么要把方法名算進去?)
3、不用構造函數用Serializable就可以構造對象,看起來不大合理,這被稱為
extralinguistic?mechanism,所以當實現Serializable時應該注意維持構造函數中所維
持的那些不變狀態
4、增加了發布新版本的類時的測試負擔
5、1.4版本后,JavaBeans的持久化采用基于XML的機制,不再需要Serializable
6、設計用來被繼承的類時,盡量不實現Serializable,用來被繼承的interface也不要
繼承Serializable。但是如果父類不實現Serializable接口,子類很難實現它,特別是
對于父類沒有可以訪問的不含參數的構造函數的時候。所以,一旦你決定不實現
Serializable接口并且類被用來繼承的時候記得提供一個無參數的構造函數
7、內部類還是不要實現Serializable好了,除非是static的,(偶也覺得內部類不適合
用來干這類活的)
8、使用一個自定義的序列化方法
? 看看下面這個保存一個雙向鏈表的例子:
public?class?StringList?implements?Serializable
{
?private?int?size = 0;
?private?Entry head = null;
?
?private?static?class?Entry implements Serializable
?{
??String?data;
? Entry next;
? Entry previous;
?}
?...//Remainder ommitted
}
這樣會導致鏈表的每個元素以及元素之間的關系(雙向鏈表之間的連接)
都保存下來,更好的方法是提供一個自定義的序列化如下:
//String List with a resonable custom serialized form
class?StringList?implements?Serializable
{
??private?transient?int?size = 0;?????? //!transient
??private?transient?Entry head =?null;? //!transient
??
? //no longer serializable!
??private?static?class?Entry
? {
??? String data;
??? Entry next;
??? Entry previous;
? }
??
? //Appends the specified string to the list
??public?void?add(String?s) {/*...*/};
??
? /**
?? * Serialize this?StringList?instance?
?? * @author yuchifang
?? * @serialData The size of the list (the number of strings
?? * it contains) is emitted(int), in the proper sequence
?? */
??private?void?writeObject(ObjectOutputStream s)
???????????????throws?IOException
? {
??? s.defaultWriteObject();
??? s.writeInt(size);
??? //Write out all elements in the proper order
????for?(Entry e = head; e != null; e = e.next)
????? s.writeObject(e.data);
? }
??
??private?void?readObject(ObjectInputStream s)
???????????????throws?IOException, ClassNotFoundException
? {
????int?numElements = s.readInt();
????
??? //Read in all elements andd insert them in list
????for?(int?i = 0; i < numElements; i++)
????? add((String)s.readObject());
? }
? //...remainder omitted
}
9、不管你選擇什么序列化形式,聲明一個顯式的UID:
private static final long serialVersionUID = randomLongValue;
10、不需要序列化的東西使用transient注掉它吧,別什么都留著
11、writeObject/readObject重載以完成更好的序列化
readResolve 與 writeReplace重載以完成更好的維護invariant controllers
MarshalByRefObject和Serializable
最近在看web sevice 方面的東西,順便看了下序列化,懂了不少啊 :
從MarshalByRefObject派生的類和有[Serializable]的類都可以跨越應用程序域作為參數傳遞。
從MarshalByRefObject派生的類按引用封送,有[Serializable]標志的類,按值封送。
如果此類即從MarshalByRefObject派生,也有[Serializable]標志也是按引用封送。
序列化有3種情況:
在webservice里,寫個web method,傳個自定義類做參數,就是這種情況。系統會幫你搞定,把自定義的類轉換為默認XML格式。
要加[Serializable]標志,可以把私有變量和公共變量都序列化。
需要實現ISerializable接口,定義序列化函數ISerializable.GetObjectData,和還原序列化的構造函數。
一個soap參數類的sample:
????public?class?serialze:ISerializable?
????{
????????//?序列化函數,由?SoapFormatter?在序列化過程中調用
????????void?ISerializable.GetObjectData(SerializationInfo?info,?StreamingContext
????????????ctxt)
????????{
????????????//?向?SerializationInfo?對象中添加每個字段
????????????info.AddValue("UserName",?UserName);
????????????info.AddValue("UserID",UserID);
????????}
????????//?還原序列化構造函數,由?SoapFormatter?在還原序列化過程中調用
????????public?serialze(SerializationInfo?info,?StreamingContext?ctxt)
????????{
????????????//?從?SerializationInfo?對象中還原序列化出各個字段
????????????UserName?=?(string)info.GetValue("UserName",?typeof(string));
????????????UserID?=?(int)?info.GetValue("UserID",typeof(int));
????????}
??
????????public?serialze()
????????{}
????????public?string?UserName;
????????public?int?UserID;
????}
是的,如果Session要存到數據庫中就必須添加Serializable標記~
from:?http://blog.csdn.net/it_man/article/details/1387991
《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀總結
以上是生活随笔為你收集整理的Serializable java序列化的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入字节码操作:使用ASM和Javass
- 下一篇: 深入Struts