一.對象序列化是什么情妖?
對象總是在運行時存在,當程序終止時诱担,對象也就無論如何也不存在了毡证。而序列化即提供了一種機制,通過將“對象持久化”蔫仙,可以讓對象在程序不運行時也能存在料睛。
二.對象序列化的用途是什么?
- java遠程方法調(diào)用(
RMI
):可以使得遠程對象的像在本機上存在一樣。當向遠程對象發(fā)送消息是恤煞,需要通過對象序列化來傳出參數(shù)和返回值屎勘。 - Java Beans:使用bean時需要在設計階段對它的狀態(tài)信息進行配置。這種狀態(tài)信息會保存下來居扒,并在程序啟動時進行恢復概漱。
三.保存和加載序列化對象
為了保存對象數(shù)據(jù),需要打開一個ObjectOutputStream對象:
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("filename"))
為了保存對象喜喂,可以直接用ObjectOutputStream 的writeObject方法
Employee harry = new Employee("Harry Hacker",50000,1989,10,1);
out.writeObject(harry);
為了讀回對象瓤摧,首先要獲得一個ObjectInputStream對象:
ObjectInputStream in = new ObjectInputStream(new FileInputStream("filename"))
然后,用readObject方法以這些對象寫入順序獲取他們:
Employee e1 = (Employee)in.readObject();
注意:保存和加載基本數(shù)據(jù)類型時夜惭,使用基本數(shù)據(jù)類型對應的方法姻灶,如int類型,使用writeInt和readInd诈茧。
四.序列化會持久化對象哪些內(nèi)容产喉?
1.對象序列化不僅保存了對象的“全景圖”,而且能追蹤對象對所包含的所有引用敢会,并保存那些對象曾沈;接著又能對對象內(nèi)每個引用進行追蹤,找到它的引用類鸥昏,以此類推塞俱,形成一個“對象網(wǎng)”。
2.對象序列化是以特殊格式存儲對象數(shù)據(jù)的吏垮,如果有興趣的可以深入了解“對象序列化的文件格式”障涯。這里只做簡單介紹:
存儲對象時,對象所屬類也必須要存儲膳汪。這個類的描述包括:
- 類名
- 序列化的版本唯一ID唯蝶,他是數(shù)據(jù)域類型和方法簽名的指紋。
- 描述序列化方法的標志集遗嗽。
- 對數(shù)據(jù)域的描述
值得注意的點:任何類的完整的類描述符只保存一次粘我,后續(xù)描述符將引用它(通過序列化唯一ID)。
五.多對象引用的情況
基于“對象網(wǎng)”特性痹换,如果存在存在多個對象對同一個對象進行引用征字,如兩個經(jīng)理公用同一個秘書:
Employee harry = new Employee("Harry Hacker",50000,1989,10,1);
Manager carl = new Manager (...);
carl.setSecretary(harry);
Manager tony= new Manager (...);
tony.setSecretary(harry);
這種情況是無法通過保存對象的內(nèi)存地址來保證秘書是同一個人,因為當對象被重載時娇豫,他可能重新占據(jù)一塊新的地址匙姜。
序列化保證每個對象都是用同一個序列號保存。下面是它的算法:
- 對遇到的每個對象引用都關(guān)聯(lián)一個序列號
- 對于每個對象冯痢,讓第一次遇到時氮昧,保存其對象數(shù)據(jù)到輸出流中
- 如果某個對象之前被保存過或详,那么只寫出“與之前保存的序列號為x的對象相同”
在讀回對象時,則是反向操作: - 輸入流的對象郭计,在第一次遇到其序列號時構(gòu)建它,并使用流中數(shù)據(jù)進行初始化椒振,然后記錄序列號與對象之間的關(guān)聯(lián)
- 當遇到“與之前保存的序列號為x的對象相同”標記時昭伸,獲取與這個序列號相同的引用
注意:只是同一流中的同一對象會添加“與之前保存的序列號為x的對象相同”標記。不同流時無法保證的澎迎。
六.對象序列化的其他方法
上面只介紹了實現(xiàn)Serializable接口的常用序列化方法庐杨,序列化包括以下幾種方法:
- 含有writeObject方法的類
- 實現(xiàn)了Serializable接口的類
- 實現(xiàn)了Externalizable接口的類
七.屏蔽對象中某些域的方式
有時候我們不希望對象所有的域都被序列化,如用戶的密碼字段夹供。
這種情況序列化提供了一個transient
(瞬時)關(guān)鍵字灵份,通過對對象中的某些不想要序列化的域添加關(guān)鍵字,即可以保證域不被序列化
還有另外一種處理這種情況的方式--使用Externalizable序列化哮洽,后續(xù)會進行介紹
八.修改默認的序列化機制
通常我們不需要添加額外的方法填渠,Serializable序列化方式就可以自動實現(xiàn)對對象的序列化。但序列化機制也提供了另外一種方式鸟辅,來屏蔽自動序列化氛什,向默認的讀寫行為添加驗證或其他行為。
private void readObject(ObjectInputStream in) throws IOException,ClassNotFoundException;
private void writeObject(ObjectOutputStream in) throws IOException;
通常匪凉,在方法的內(nèi)部我們需要添加defaultWriteObject
方法和defaultReadObject
方法來保存和加載默認Serializable序列化方式中自動處理的字段(即非transient
字段)枪眉,而對于transient
字段,則需要通過writeObject
方法和readObject
方法來進行保存和加載(是的再层,你沒有看錯贸铜,transient
字段只是無法被默認序列化方式保存和加載)。
九.Externalizable序列化方式
Externalizable序列化方式與Serializable序列化方式的不同點在于:
- Externalizable序列化方式不會自動完成序列化聂受,而需要自己定義那些域需要序列化蒿秦,Externalizable提供了兩個方法
writeExternal
和readExternal
,分別顯式的保存和加載域饺饭。 - Externalizable序列化方式會調(diào)用構(gòu)造器渤早。對于Serializable對象,對象完全以它存儲的二進制位為基礎(chǔ)來構(gòu)造瘫俊,而Externalizable對象則會調(diào)用所有默認的構(gòu)造器(包括字段定義時的初始化鹊杖,如果無法加載出數(shù)據(jù),會初始化為null值)