一 簡介
對象序列化就是把一個對象變成二進制的數據流的一種方法,通過對象序列化可以方便地實現對象的傳輸和存儲。
把對象轉換為字節序列的過程稱為對象的序列化
把字節序列恢復為對象的過程稱為對象的反序列化
對象的序列化主要有兩種用途:
1) 把對象的字節序列永久地保存到硬盤上,通常存放在一個文件中;
2) 在網絡上傳送對象的字節序列。
對象序列化包括如下步驟:
1) 創建一個對象輸出流,它可以包裝一個其他類型的目標輸出流,如文件輸出流;
2) 通過對象輸出流的writeObject()方法寫對象。
對象反序列化包括如下步驟:
1) 創建一個對象輸入流,它可以包裝一個其他類型的源輸入流,如文件輸入流;
2) 通過對象輸入流的readObject()方法讀取對象。
在很多應用中,需要對某些對象進行序列化,讓它們離開內存空間,入住物理硬盤,以便長期保存。比如最常見的是Web服務器中的Session對象,當有 10萬用戶并發訪問,就有可能出現10萬個Session對象,內存可能吃不消,于是Web容器就會把一些seesion先序列化到硬盤中,等要用了,再把保存在硬盤中的對象還原到內存中。
當兩個進程在進行遠程通信時,彼此可以發送各種類型的數據。無論是何種類型的數據,都會以二進制序列的形式在網絡上傳送。發送方需要把這個Java對象轉換為字節序列,才能在網絡上傳送;接收方則需要把字節序列再恢復為Java對象。
二 Java中的序列化API
如果一個類的對象想被序列化,那么該對象所在的類必須實現java.io.Serializable接口,此接口的定義如下:
public interface Serializable{}
可以發現此接口并沒有定義任何的方法,只是一個標識接口,表示一個類可以被序列化,如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | package ?cn.zifangsky.serializable; import ?java.io.Serializable; public ?class ?Person? implements ?Serializable?{ ???? private ?static ?final ?long ?serialVersionUID?=?2651243789670519969L; ???? private ?String?name; ???? private ?int ?age; ???? public ?Person(String?name,? int ?age)?{ ???????? this .name?=?name; ???????? this .age?=?age; ???? } ???? public ?String?toString()?{ ???????? return ?"Person?[name=" ?+?name?+? ",?age=" ?+?age?+? "]" ; ???? } } |
就像上面這個Person類一樣,實現了序列化接口表明此類的對象可以經過二進制的數據流進行傳輸了。但是如果想要完成對象的輸入和輸出,還需要借助對象輸出流(ObjectOutputStream)和對象輸入流(ObjectInputStream)
java.io.ObjectOutputStream代表對象輸出流,它的writeObject(Object obj)方法可對參數指定的obj對象進行序列化,把得到的字節序列寫到一個目標輸出流中;java.io.ObjectInputStream代表對象輸入流,它的readObject()方法從一個源輸入流中讀取字節序列,再把它們反序列化為一個對象,并將其返回
只有實現了Serializable或Externalizable接口的類的對象才能被序列化。Externalizable接口繼承自 Serializable接口,實現Externalizable接口的類完全由自身來控制序列化的行為,而僅實現Serializable接口的類可以 采用默認的序列化方式 。
三 使用Serializable接口實現的實例
首先定義了一個實現了Serializable接口WebSite的實體類
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | package ?cn.zifangsky.serializable; import ?java.io.Serializable; public ?class ?WebSite? implements ?Serializable?{ ???? private ?static ?final ?long ?serialVersionUID?=?1835573222135484360L; ???? private ?String?siteName; ???? private ?String?siteUrl; ???? private ?String?webMaster; ???? public ?WebSite()?{ ???? } ???? public ?WebSite(String?siteName,?String?siteUrl,?String?webMaster)?{ ???????? this .siteName?=?siteName; ???????? this .siteUrl?=?siteUrl; ???????? this .webMaster?=?webMaster; ???? } ???? public ?String?getSiteName()?{ ???????? return ?siteName; ???? } ???? public ?void ?setSiteName(String?siteName)?{ ???????? this .siteName?=?siteName; ???? } ???? public ?String?getSiteUrl()?{ ???????? return ?siteUrl; ???? } ???? public ?void ?setSiteUrl(String?siteUrl)?{ ???????? this .siteUrl?=?siteUrl; ???? } ???? public ?String?getWebMaster()?{ ???????? return ?webMaster; ???? } ???? public ?void ?setWebMaster(String?webMaster)?{ ???????? this .webMaster?=?webMaster; ???? } ???? public ?String?toString()?{ ???????? return ?"WebSite?[siteName=" ?+?siteName?+? ",?siteUrl=" ?+?siteUrl?+? ",?webMaster=" ?+?webMaster?+? "]" ; ???? } } |
然后進行序列化和反序列化測試:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | package ?cn.zifangsky.serializable; import ?java.io.File; import ?java.io.FileInputStream; import ?java.io.FileNotFoundException; import ?java.io.FileOutputStream; import ?java.io.IOException; import ?java.io.ObjectInputStream; import ?java.io.ObjectOutputStream; public ?class ?TestSerialize?{ ???? public ?static ?void ?main(String[]?args)? throws ?FileNotFoundException,?IOException,?ClassNotFoundException?{ ???????? TestSerialize.serializeWebSite(); ???????? TestSerialize.deserializeWebSite(); ???? } ???? ????? /** ????? *?使用ObjectOutputStream?序列化WebSite ????? *?@throws?IOException? ????? *?@throws?FileNotFoundException? ????? *? ????? *?*/ ???? public ?static ?void ?serializeWebSite()? throws ?FileNotFoundException,?IOException{ ???????? WebSite?webSite?=? new ?WebSite(); ???????? webSite.setSiteName( "zifangsky的個人博客" ); ???????? webSite.setSiteUrl( "http://www.zifangsky.cn" ); ???????? webSite.setWebMaster( "zifangsky" ); ???????? ????????? //序列化 ???????? ObjectOutputStream?objectOutputStream?=? new ?ObjectOutputStream( new ?FileOutputStream( new ?File( "C:/Users/Administrator/Desktop/test.txt" ))); ???????? objectOutputStream.writeObject(webSite); ???????? objectOutputStream.flush(); ???????? objectOutputStream.close(); ???? } ???? ????? /** ????? *?使用ObjectInputStream?反序列化WebSite ????? *?@throws?IOException? ????? *?@throws?FileNotFoundException? ????? *?@throws?ClassNotFoundException? ????? *? ????? *?*/ ???? public ?static ?void ?deserializeWebSite()? throws ?FileNotFoundException,?IOException,?ClassNotFoundException{ ???????? ObjectInputStream?objectInputStream?=? new ?ObjectInputStream( new ?FileInputStream( new ?File( "C:/Users/Administrator/Desktop/test.txt" ))); ???????? //反序列化 ???????? WebSite?webSite?=?(WebSite)?objectInputStream.readObject(); ???????? objectInputStream.close(); ???????? System.out.println(webSite); ???? } ???? ?} |
輸出:
1 | WebSite?[siteName=zifangsky的個人博客,?siteUrl=http://www.zifangsky.cn,?webMaster=zifangsky] |
四?使用Externalizable接口實現的實例
被Serializable接口聲明的類的對象的內容都將被序列化,如果現在用戶希望自己制定序列化的內容,則可以讓一個類實現Externalizable接口,此接口的定義如下:
public interface Externalizable extends java.io.Serializable {
?
? ? void writeExternal(ObjectOutput out) throws IOException;
? ? void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}
其中,這兩個方法的作用是:
i)writeExternal(ObjectOutput out)?:在此方法中制定要保存的屬性信息,對象序列化時調用
ii)readExternal(ObjectInput in)?: 在此方法中讀取被保存的信息,對象反序列化時調用
下面將以一個具體的實例來簡單說明序列化和反序列化過程:
(1)實現了Externalizable接口的實體類:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | package ?cn.zifangsky.serializable; import ?java.io.Externalizable; import ?java.io.IOException; import ?java.io.ObjectInput; import ?java.io.ObjectOutput; public ?class ?ExternalizableDemo? implements ?Externalizable{ ???? private ?String?name; ???? static ?{ ???????? System.out.println( "調用靜態代碼塊" ); ???? } ???? public ?ExternalizableDemo()?{ ???????? System.out.println( "調用無參構造方法" ); ???? } ???? public ?ExternalizableDemo(String?name)?{ ???????? this .name?=?name; ???????? System.out.println( "調用有參構造方法" ); ???? } ???? public ?String?getName()?{ ???????? return ?name; ???? } ???? public ?void ?setName(String?name)?{ ???????? this .name?=?name; ???? } ???? public ?String?toString()?{ ???????? return ?"ExternalizableDemo?[name=" ?+?name?+? "]" ; ???? } ???? ????? /** ????? *?ObjectOutputStream會調用writeExternal(ObjectOutput?out))這個方法進行序列化 ????? *?*/ ???? public ?void ?writeExternal(ObjectOutput?out)? throws ?IOException?{ ???????? out.writeObject(name); ???? } ???? /** ????? *?ObjectInputStream會調用readExternal(ObjectInput?in)這個方法進行反序列化 ????? *?*/ ???? public ?void ?readExternal(ObjectInput?in)? throws ?IOException,?ClassNotFoundException?{ ???????? name?=?(String)?in.readObject(); ???? } } |
(2)然后進行序列化和反序列化測試:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | package ?cn.zifangsky.serializable; import ?java.io.File; import ?java.io.FileInputStream; import ?java.io.FileNotFoundException; import ?java.io.FileOutputStream; import ?java.io.IOException; import ?java.io.ObjectInputStream; import ?java.io.ObjectOutputStream; public ?class ?TestExternalizable?{ ???? public ?static ?void ?main(String[]?args)? throws ?FileNotFoundException,?IOException,?ClassNotFoundException?{ ???????? ExternalizableDemo?demo?=? new ?ExternalizableDemo( "hello" ); ???????? //?序列化 ???????? ObjectOutputStream?objectOutputStream?=? new ?ObjectOutputStream( ???????????????? new ?FileOutputStream( new ?File( "C:/Users/Administrator/Desktop/test2.txt" ))); ???????? objectOutputStream.writeObject(demo); ???????? objectOutputStream.flush(); ???????? objectOutputStream.close(); ???????? ????????? //反序列化 ???????? ObjectInputStream?objectInputStream?=? new ?ObjectInputStream( new ?FileInputStream( new ?File( "C:/Users/Administrator/Desktop/test2.txt" ))); ???????? ExternalizableDemo?demo2?=?(ExternalizableDemo)?objectInputStream.readObject(); ???????? objectInputStream.close(); ???????? System.out.println(demo2);????? ???? } } |
輸出:
1 2 3 4 | 調用靜態代碼塊 調用有參構造方法 調用無參構造方法 ExternalizableDemo?[name=hello] |
注:
(1)使用Externalizable進行序列化時,當讀取對象時,會調用被序列化類的無參構造器去創建一個新的對象,然后再將被保存對象的字段的值分別填充到新對象中
(2)Externalizable接口與Serializable接口實現序列化的區別:
區別 | Serializable | Externalizable |
實現負責度 | 實現簡單,Java對其有內建支持 | 實現負責,由開發人員自己完成 |
執行效率 | 所有對象由Java統一保存,性能較低 | 開發人員自己決定保存那些對象,可能造成速度提升 |
保存信息 | 保存時占用空間大 | 部分存儲,可能造成空間減小 |
(3)一個對象被序列化后,到底哪些內容被保存了下來,是屬性還是方法?
答:只有屬性被序列化。因為每個對象都有相同的方法,但是每個對象的屬性卻不一定相同,因此對象保存的只有屬性信息,那么同樣道理在進行序列化操作時也只有屬性被序列化
五 transient關鍵字
當使用Serializable接口實現序列化操作時,如果一個對象中的某個屬性不希望被序列化,那么就可以使用transient關鍵字進行聲明
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | package ?cn.zifangsky.serializable; import ?java.io.Serializable; public ?class ?WebSite? implements ?Serializable?{ ???? private ?static ?final ?long ?serialVersionUID?=?1835573222135484360L; ???? private ?String?siteName; ???? private ?String?siteUrl; ???? private ?transient ?String?webMaster; ???? public ?WebSite(String?siteName,?String?siteUrl,?String?webMaster)?{ ???????? this .siteName?=?siteName; ???????? this .siteUrl?=?siteUrl; ???????? this .webMaster?=?webMaster; ???? } ???? public ?String?toString()?{ ???????? return ?"WebSite?[siteName=" ?+?siteName?+? ",?siteUrl=" ?+?siteUrl?+? ",?webMaster=" ?+?webMaster?+? "]" ; ???? } } |
在上面這個類中,不希望webMaster這個屬性被序列化,因此把它用transient關鍵字進行修飾,接下來就是序列化與反序列化測試:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | package ?cn.zifangsky.serializable; import ?java.io.File; import ?java.io.FileInputStream; import ?java.io.FileNotFoundException; import ?java.io.FileOutputStream; import ?java.io.IOException; import ?java.io.ObjectInputStream; import ?java.io.ObjectOutputStream; public ?class ?TestSerialize?{ ???? public ?static ?void ?main(String[]?args)? throws ?FileNotFoundException, ???????????? IOException,?ClassNotFoundException?{ ???????? TestSerialize.serializeWebSite(); ???????? TestSerialize.deserializeWebSite(); ???? } ???? /** ????? *?使用ObjectOutputStream?序列化WebSite ????? *? ????? *?@throws?IOException ????? *?@throws?FileNotFoundException ????? *? ????? *?*/ ???? public ?static ?void ?serializeWebSite()? throws ?FileNotFoundException, ???????????? IOException?{ ???????? WebSite?webSite?=? new ?WebSite( "zifangsky的個人博客" , "http://www.zifangsky.cn" , "zifangsky" ); ???????? //?序列化 ???????? ObjectOutputStream?objectOutputStream?=? new ?ObjectOutputStream( ???????????????? new ?FileOutputStream( new ?File( ???????????????????????? "C:/Users/Administrator/Desktop/test.txt" ))); ???????? objectOutputStream.writeObject(webSite); ???????? objectOutputStream.flush(); ???????? objectOutputStream.close(); ???? } ???? /** ????? *?使用ObjectInputStream?反序列化WebSite ????? *? ????? *?@throws?IOException ????? *?@throws?FileNotFoundException ????? *?@throws?ClassNotFoundException ????? *? ????? *?*/ ???? public ?static ?void ?deserializeWebSite()? throws ?FileNotFoundException, ???????????? IOException,?ClassNotFoundException?{ ???????? ObjectInputStream?objectInputStream?=? new ?ObjectInputStream( ???????????????? new ?FileInputStream( new ?File( ???????????????????????? "C:/Users/Administrator/Desktop/test.txt" ))); ???????? //?反序列化 ???????? WebSite?webSite?=?(WebSite)?objectInputStream.readObject(); ???????? objectInputStream.close(); ???????? System.out.println(webSite); ???? } } |
輸出:
1 | WebSite?[siteName=zifangsky的個人博客,?siteUrl=http://www.zifangsky.cn,?webMaster=null] |
從上面的輸出可以看出,webMaster這個屬性并沒有被序列化
本文轉自 pangfc 51CTO博客,原文鏈接:http://blog.51cto.com/983836259/1758247,如需轉載請自行聯系原作者