什么是序列化
(1)序列化是將對(duì)象轉(zhuǎn)變?yōu)樽止?jié)序列的過程,反序列化則是將字節(jié)序列恢復(fù)為對(duì)象的過程。
(2)對(duì)象序列化保存的是對(duì)象的狀態(tài),即它的成員變量;
(3)對(duì)象的持久化存儲(chǔ)(寫文件),網(wǎng)絡(luò)傳輸對(duì)象,或者使用RMI都會(huì)用到對(duì)象序列化。
JAVA 提供的操作序列化的接口
(1)Java 主要提供給了兩個(gè)接口實(shí)現(xiàn)對(duì)象的序列化和反序列化,java.io.ObjectInputStream的readObject()方法?和 java.io.ObjectOutputStream 的writeObject(Object obj)方法;
(2)只有實(shí)現(xiàn)Serializable或Externalizable接口的類的對(duì)象才能被序列化;否則會(huì)拋出java.io.NotSerializableException異常。
JAVA對(duì)象序列化示例
(1)類實(shí)現(xiàn) Serializable接口
?類中未定義 writeObject(Object obj)和readObject方法,那么按照默認(rèn)的序列化方式實(shí)現(xiàn)序列化和反序列化。
以上代碼展示了如何序列化對(duì)象到一個(gè)文件中并從文件中反序列化的過程。
序列化的過程:
首先創(chuàng)建 ObjectOutputStream 對(duì)象,該對(duì)象可以包裝其他輸出流,比如文件輸出流;
調(diào)用對(duì)象輸出流的writeObject(Object obj)方法,可以將對(duì)象寫入到輸出流中。
關(guān)閉流。結(jié)束。
對(duì)象持久化到文件中的過程結(jié)束。
反序列化的過程:
首先創(chuàng)建ObjectInputStream對(duì)象,類似于ObjectOutputStream;
調(diào)用對(duì)象輸入流的readObject()方法,讀對(duì)象到輸入流中。返回字節(jié)序列轉(zhuǎn)化的對(duì)象。
關(guān)閉流;結(jié)束。
輸出:
?類中定義了 writeObject(Object obj)和readObject方法,那么按照自定義的序列化方實(shí)現(xiàn)式序列化和反序列化。
在Student.java添加如下兩個(gè)方法:
輸出結(jié)果:
(2)transient?關(guān)鍵字
? 當(dāng)某個(gè)成員變量聲明為transient后,默認(rèn)的序列化機(jī)制就會(huì)忽略該變量。
將age字段聲明為transient,
輸出 age=0:
此時(shí)我們可以選擇單獨(dú)傳輸某個(gè)字段;修改writeObject和readObject方法:
結(jié)果:
單獨(dú)傳輸了age 字段,因此age=18;
補(bǔ)充:除了上面提到的兩個(gè)方法外:
private void writeObject(java.io.ObjectOutputStream out)?throws IOException ;
private void readObject(java.io.ObjectInputStream in)?throws IOException, ClassNotFoundException;
還有其他三個(gè)方法,可供我們定制自己的序列化反序列化過程:
private void readObjectNoData()?throws ObjectStreamException;
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
readObjectNoData() :用于初始化反序列化對(duì)象,當(dāng)發(fā)生一些情況導(dǎo)致反序列化對(duì)象不能獲得數(shù)據(jù)時(shí)調(diào)用;
writeReplace() :指派其他對(duì)象寫入序列化的流中;
readResolve():返回的對(duì)象替換反序列化創(chuàng)建的實(shí)例;
readResolve() 常用于單例模式中;示例:
修改Student.java,添加instanceHoder:
修改SimpleSerial.java:
結(jié)果輸出:
可以看到,s==student返回false,也就是說反序列化后得到的Student對(duì)象并不是唯一的instance,因此這樣寫單例模式是失敗的;
修正:
再次運(yùn)行:
總結(jié):
當(dāng)進(jìn)行序列化的時(shí)候:
首先JVM會(huì)先調(diào)用writeReplace方法,在這個(gè)階段,我們可以進(jìn)行張冠李戴,將需要進(jìn)行序列化的對(duì)象換成我們指定的對(duì)象.
跟著JVM將調(diào)用writeObject方法,來將對(duì)象中的屬性一個(gè)個(gè)進(jìn)行序列化,我們可以在這個(gè)方法中控制住哪些屬性需要序列化.
當(dāng)反序列化的時(shí)候:
JVM會(huì)調(diào)用readObject方法,將我們剛剛在writeObject方法序列化好的屬性,反序列化回來.
然后在readResolve方法中,我們也可以指定JVM返回我們特定的對(duì)象(不是剛剛序列化回來的對(duì)象).
注意到在writeReplace和readResolve,我們可以嚴(yán)格控制singleton的對(duì)象,在同一個(gè)JVM中完完全全只有唯一的對(duì)象,控制不讓singleton對(duì)象產(chǎn)生副本.
(3)類實(shí)現(xiàn)Externalizable 接口?
? ?Externalizable 接口繼承 Serializable接口:?
public interface Externalizable extends?Serializable?;
Serializable接口是一個(gè)mark interface,沒有實(shí)際方法;而Externalizable 接口提供了兩個(gè)方法:
void ?readExternal?(ObjectInput?in) ;
void ?writeExternal?(ObjectOutput?out) ;
readExternal (ObjectInput in):從輸入流中讀取內(nèi)容恢復(fù)對(duì)象;
writeExternal?(ObjectOutput?out) : 寫入對(duì)象到輸出流中;
示例:
使用Externalizable進(jìn)行序列化時(shí),當(dāng)讀取對(duì)象時(shí),會(huì)調(diào)用被序列化類的無參構(gòu)造器去創(chuàng)建一個(gè)新的對(duì)象,然后再將被保存對(duì)象的字段的值分別填充到新對(duì)象中。因此,必須提供一個(gè)無參構(gòu)造器,訪問權(quán)限為public;否則會(huì)拋出java.io.InvalidClassException 異常;
總結(jié):Externalizable接口實(shí)現(xiàn)的功能與Serializable接口類似,Serializable序列化時(shí)不會(huì)調(diào)用默認(rèn)的構(gòu)造器,而Externalizable序列化時(shí)會(huì)調(diào)用默認(rèn)構(gòu)造器;