Serializable java序列化
Bean Serializable Interface 的接口讓BEAN可以串行化,將其變成一個可保存為以后使用的二進制流。當(dāng)一個BEAN被系列化到磁盤上或者其他任何地方,其狀態(tài)被保存起來,其中的屬性值也不會改變。在BEAN的規(guī)范中,JSP并沒有要求BEAN實現(xiàn)Serializable接口。但是,如果您希望自己控制您所創(chuàng)建的組件的serialization進程,或者您想serialize并不是標(biāo)準(zhǔn)組件擴展的組件,您必須了解serialization and deserialization的細(xì)節(jié)。
有幾個原因你會把BEAN冷藏起來以備后用。有些服務(wù)器通過將所有的SESSION 數(shù)據(jù)(包括BEAN)寫入磁盤來支持任意長的SESSION生命期,即使服務(wù)器停機也不會丟失。當(dāng)服務(wù)器重新啟動后,串行化的數(shù)據(jù)被恢復(fù)。同樣的理由,在重負(fù)載的站點上支持服務(wù)器分簇的環(huán)境中,許多服務(wù)器通過串行化來復(fù)制SESSION。如果你的BEAN不支持串行化,服務(wù)器就不能正確地保存和傳輸類。
通過同樣的策略,你可以選擇將BEAN保存在磁盤上或者數(shù)據(jù)庫中,以備后用。例如,也許可以將客戶的購物車實現(xiàn)為一個BEAN,在訪問期間將其保存在數(shù)據(jù)庫中。
如果BEAN需要特殊的復(fù)雜的初始設(shè)置,可以將BEAN設(shè)置好后串行化保存在磁盤上。這個BEAN的“快照”可以用在任何需要的地方,包括在$#@60;jsp:useBean$#@62;中用beanName屬性的調(diào)用。
$#@60;jsp:useBean$#@62;標(biāo)簽中的beanName屬性,用來實例化一個串行化的BEAN,而不是用來從一個類創(chuàng)建一個全新的實例。如果BEAN還沒有創(chuàng)建,beanName屬性傳給Java.beans.Bean.instantiate()方法,由類裝載器對類進行實例化。它首先假定存在一個串行化的BEAN(帶有擴展名.ser),然后會將其激活。如果這個操作失敗,它就會實例化一個新的實例。
下面簡單介紹一下這個接口:
對象能包含其它的對象,而這其它的對象又可以包含另外的對象。Java?serialization能夠自動的處理嵌套的對象。對于一個對象的簡單的域,writeObject()直接將值寫入流。而,當(dāng)遇到一個對象域時,writeObject()被再次調(diào)用,如果這個對象內(nèi)嵌另一個對象,那么,writeObject() 又被調(diào)用,直到對象能被直接寫入流為止。程序員所需要做的是將對象傳入ObjectOutputStream 的writeObject() 方法,剩下的將又系統(tǒng)自動完成。下面的例子創(chuàng)建了一個調(diào)用mine對象的PersonalData對象。代碼實現(xiàn)的是將一個串和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(); // 清空并關(guān)閉流
...
一個FileOutputStream對象被創(chuàng)建且傳到一個ObjectOutputStream。當(dāng)out.writeObject() 被調(diào)用,這個串和mine 對象被objects are serializ順序加入一個存入文件PersonalData.ser的字節(jié)對列。
您應(yīng)該注意上述類是實現(xiàn)的java.io.Serializable接口。因為它并未指定要實現(xiàn)的方法,所以Serializable被稱為"tagging interface" ,但是它僅僅"tags"它自己的對象是一個特殊的類型。任一個您希望serialize的對象都應(yīng)該實現(xiàn)這個接口。這是必須的。否則,用到流技術(shù)時將根本不工作。例如,如果您試著去serialize 一個沒有實現(xiàn)這個接口的對象,一個 NotSerializableException將產(chǎn)生。
?
類通過實現(xiàn) java.io.Serializable 接口以啟用其序列化功能。未實現(xiàn)此接口的類將無法使其任何狀態(tài)序列化或反序列化。可序列化類的所有子類型本身都是可序列化的。序列化接口沒有方法或字段,僅用于標(biāo)識可序列化的語義。
Java的"對象序列化"能讓你將一個實現(xiàn)了Serializable接口的對象轉(zhuǎn)換成一組byte,這樣日后要用這個對象時候,你就能把這些byte數(shù)據(jù)恢復(fù)出來,并據(jù)此重新構(gòu)建那個對象了。
要想序列化對象,你必須先創(chuàng)建一個OutputStream,然后把它嵌進ObjectOutputStream。這時,你就能用writeObject( )方法把對象寫入OutputStream了。
writeObject 方法負(fù)責(zé)寫入特定類的對象的狀態(tài),以便相應(yīng)的 readObject 方法可以還原它。通過調(diào)用 out.defaultWriteObject 可以調(diào)用保存 Object 的字段的默認(rèn)機制。該方法本身不需要涉及屬于其超類或子類的狀態(tài)。狀態(tài)是通過使用 writeObject 方法或使用 DataOutput 支持的用于基本數(shù)據(jù)類型的方法將各個字段寫入 ObjectOutputStream 來保存的。
讀的時候,你得把InputStream嵌到ObjectInputStream里面,然后再調(diào)用readObject( )方法。不過這樣讀出來的,只是一個Object的reference,因此在用之前,還得先下傳。readObject 方法負(fù)責(zé)從流中讀取并還原類字段。它可以調(diào)用 in.defaultReadObject 來調(diào)用默認(rèn)機制,以還原對象的非靜態(tài)和非瞬態(tài)字段。
defaultReadObject 方法使用流中的信息來分配流中通過當(dāng)前對象中相應(yīng)命名字段保存的對象的字段。這用于處理類發(fā)展后需要添加新字段的情形。該方法本身不需要涉及屬于其超類或子類的狀態(tài)。狀態(tài)是通過使用 writeObject 方法或使用 DataOutput 支持的用于基本數(shù)據(jù)類型的方法將各個字段寫入 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???{?
?????????????/*??創(chuàng)建一個文件寫入序列化樹。??*/??
????????????FileOutputStream?ostream??=???new??FileOutputStream(?"?tree.tmp?"?);?
?????????????/*??創(chuàng)建輸出流??*/??
????????????ObjectOutputStream?p??=???new??ObjectOutputStream(ostream);?
?????????????/*??創(chuàng)建一個二層的樹。??*/??
????????????tree?base??=???new??tree(?2?);?
????????????p.writeObject(base);??//??將樹寫入流中。??
?????????????p.writeObject(?"?LiLy?is?惠止南國?"?);
????????????p.flush();?
????????????ostream.close();?????//??關(guān)閉文件。??
?
??????????????/*??打開文件并設(shè)置成從中讀取對象。??*/??
????????????FileInputStream?istream??=???new??FileInputStream(?"?tree.tmp?"?);?
????????????ObjectInputStream?q??=???new??ObjectInputStream(istream);?
?????????????/*??讀取樹對象,以及所有子樹??*/??
????????????tree?new_tree??=??(tree)q.readObject();?
????????????new_tree.print(?2?);???//??打印出樹形結(jié)構(gòu)的最上面?2級??
?????????????String?name??=??(String)q.readObject();
????????????System.out.println(?"?/n?"?+?name);
????????}???catch??(Exception?ex)??{?
????????????ex.printStackTrace();?
????????}??
????}??
}??
?
最后結(jié)果如下:
??? node 0
? node 1
node 2
node 3
? node 4
node 5
node 6
LiLy is 惠止南國?
可以看到,在序列化的時候,writeObject與readObject之間的先后順序。readObject將最先write的object read出來。用數(shù)據(jù)結(jié)構(gòu)的術(shù)語來講就姑且稱之為先進先出吧!
在序列化時,有幾點要注意的:
1:當(dāng)一個對象被序列化時,只保存對象的非靜態(tài)成員變量,不能保存任何的成員方法和靜態(tài)的成員變量。
2:如果一個對象的成員變量是一個對象,那么這個對象的數(shù)據(jù)成員也會被保存。
3:如果一個可序列化的對象包含對某個不可序列化的對象的引用,那么整個序列化操作將會失敗,并且會拋出一個NotSerializableException。我們可以將這個引用標(biāo)記為transient,那么對象仍然可以序列化
還有我們對某個對象進行序列化時候,往往對整個對象全部序列化了,比如說類里有些數(shù)據(jù)比較敏感,不希望序列化,一個方法可以用transient來標(biāo)識,另一個方法我們可以在類里重寫
??????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、實現(xiàn)Serializable回導(dǎo)致發(fā)布的API難以更改,并且使得package-private和private
這兩個本來封裝的較好的咚咚也不能得到保障了
2、Serializable會為每個類生成一個序列號,生成依據(jù)是類名、類實現(xiàn)的接口名、
public和protected方法,所以只要你一不小心改了一個已經(jīng)publish的API,并且沒有自
己定義一個long類型的叫做serialVersionUID的field,哪怕只是添加一個getXX,就會
讓你讀原來的序列化到文件中的東西讀不出來(不知道為什么要把方法名算進去?)
3、不用構(gòu)造函數(shù)用Serializable就可以構(gòu)造對象,看起來不大合理,這被稱為
extralinguistic?mechanism,所以當(dāng)實現(xiàn)Serializable時應(yīng)該注意維持構(gòu)造函數(shù)中所維
持的那些不變狀態(tài)
4、增加了發(fā)布新版本的類時的測試負(fù)擔(dān)
5、1.4版本后,JavaBeans的持久化采用基于XML的機制,不再需要Serializable
6、設(shè)計用來被繼承的類時,盡量不實現(xiàn)Serializable,用來被繼承的interface也不要
繼承Serializable。但是如果父類不實現(xiàn)Serializable接口,子類很難實現(xiàn)它,特別是
對于父類沒有可以訪問的不含參數(shù)的構(gòu)造函數(shù)的時候。所以,一旦你決定不實現(xiàn)
Serializable接口并且類被用來繼承的時候記得提供一個無參數(shù)的構(gòu)造函數(shù)
7、內(nèi)部類還是不要實現(xiàn)Serializable好了,除非是static的,(偶也覺得內(nèi)部類不適合
用來干這類活的)
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
}
這樣會導(dǎo)致鏈表的每個元素以及元素之間的關(guān)系(雙向鏈表之間的連接)
都保存下來,更好的方法是提供一個自定義的序列化如下:
//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]的類都可以跨越應(yīng)用程序域作為參數(shù)傳遞。
從MarshalByRefObject派生的類按引用封送,有[Serializable]標(biāo)志的類,按值封送。
如果此類即從MarshalByRefObject派生,也有[Serializable]標(biāo)志也是按引用封送。
序列化有3種情況:
在webservice里,寫個web method,傳個自定義類做參數(shù),就是這種情況。系統(tǒng)會幫你搞定,把自定義的類轉(zhuǎn)換為默認(rèn)XML格式。
要加[Serializable]標(biāo)志,可以把私有變量和公共變量都序列化。
需要實現(xiàn)ISerializable接口,定義序列化函數(shù)ISerializable.GetObjectData,和還原序列化的構(gòu)造函數(shù)。
一個soap參數(shù)類的sample:
????public?class?serialze:ISerializable?
????{
????????//?序列化函數(shù),由?SoapFormatter?在序列化過程中調(diào)用
????????void?ISerializable.GetObjectData(SerializationInfo?info,?StreamingContext
????????????ctxt)
????????{
????????????//?向?SerializationInfo?對象中添加每個字段
????????????info.AddValue("UserName",?UserName);
????????????info.AddValue("UserID",UserID);
????????}
????????//?還原序列化構(gòu)造函數(shù),由?SoapFormatter?在還原序列化過程中調(diào)用
????????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要存到數(shù)據(jù)庫中就必須添加Serializable標(biāo)記~
from:?http://blog.csdn.net/it_man/article/details/1387991
《新程序員》:云原生和全面數(shù)字化實踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的Serializable java序列化的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 深入字节码操作:使用ASM和Javass
- 下一篇: 深入Struts