JAVA 对象序列化(二)——Externalizable
Java默認(rèn)的序列化機(jī)制非常簡單,而且序列化后的對象不需要再次調(diào)用構(gòu)造器重新生成,但是在實(shí)際中,我們可以會希望對象的某一部分不需要被序列化,或者說一個對象被還原之后,其內(nèi)部的某些子對象需要重新創(chuàng)建,從而不必將該子對象序列化。 在這些情況下,我們可以考慮實(shí)現(xiàn)Externalizable接口從而代替Serializable接口來對序列化過程進(jìn)行控制(后面我們會講到一個更簡單的方式,通過transient的方式)。
? ? ?Externalizable接口extends?Serializable接口,而且在其基礎(chǔ)上增加了兩個方法:writeExternal()和readExternal()。這兩個方法會在序列化和反序列化還原的過程中被自動調(diào)用,以便執(zhí)行一些特殊的操作。
package java.io;import java.io.ObjectOutput; import java.io.ObjectInput;public interface Externalizable extends java.io.Serializable {void writeExternal(ObjectOutput out) throws IOException;void readExternal(ObjectInput in) throws IOException, ClassNotFoundException; }? ? ?下面這段代碼示范了如何完整的保存和恢復(fù)一個Externalizable對象
package test.serializable;import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput;public class Blip implements Externalizable {private int i ;private String s;//沒有初始化public Blip() {//默認(rèn)構(gòu)造函數(shù)必須有,而且必須是publicSystem.out.println("Blip默認(rèn)構(gòu)造函數(shù)");}public Blip(String s ,int i) {//s,i只是在帶參數(shù)的構(gòu)造函數(shù)中進(jìn)行初始化。System.out.println("Blip帶參數(shù)構(gòu)造函數(shù)");this.s = s;this.i = i;}public String toString() {return s + i ;}@Overridepublic void readExternal(ObjectInput in) throws IOException,ClassNotFoundException {System.out.println("調(diào)用readExternal()方法");s = (String)in.readObject();//在反序列化時,需要初始化s和i,否則只是調(diào)用默認(rèn)構(gòu)造函數(shù),得不到s和i的值i = in.readInt();}@Overridepublic void writeExternal(ObjectOutput out) throws IOException {System.out.println("調(diào)用writeExternal()方法");out.writeObject(s); //如果我們不將s和i的值寫入的話,那么在反序列化的時候,就不會得到這些值。out.writeInt(i);}} package test.serializable;import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream;public class ExternalizableTest {/*** @param args* @throws IOException * @throws ClassNotFoundException */public static void main(String[] args) throws IOException, ClassNotFoundException {System.out.println("序列化之前");Blip b = new Blip("This String is " , 47);System.out.println(b);System.out.println("序列化操作,writeObject");ByteArrayOutputStream out = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(out);oos.writeObject(b);System.out.println("反序列化之后,readObject");ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());ObjectInputStream ois = new ObjectInputStream(in);Blip bb = (Blip)ois.readObject();System.out.println(bb); } }運(yùn)行結(jié)果如下所示:? ? ?
序列化之前 Blip帶參數(shù)構(gòu)造函數(shù) This String is 47 序列化操作,writeObject 調(diào)用writeExternal()方法 反序列化之后,readObject Blip默認(rèn)構(gòu)造函數(shù) 調(diào)用readExternal()方法 This String is 47分析結(jié)果:
? ? ?在Blip類中,字段s和i只在第二個構(gòu)造器中初始化,而不是在默認(rèn)的構(gòu)造器其中初始化的,每次writeObject時,都會調(diào)用WriteExtenal()方法,而在WriteExtenal()方法中我們需要將當(dāng)前對象的值寫入到流中;而每次readObject()時,調(diào)用的是默認(rèn)的構(gòu)造函數(shù),如果我們不在 readExternal()方法中初始化s和i,那么s就會為null,而i就會為0。
下面分幾種情況討論:
??1) 如果我們只修改writeExternal()方法如下:
@Overridepublic void writeExternal(ObjectOutput out) throws IOException {System.out.println("調(diào)用writeExternal()方法"); // out.writeObject(s); // out.writeInt(i);}那么運(yùn)行的結(jié)果為:
序列化之前 Blip帶參數(shù)構(gòu)造函數(shù) This String is 47 序列化操作,writeObject 調(diào)用writeExternal()方法 反序列化之后,readObject Blip默認(rèn)構(gòu)造函數(shù) 調(diào)用readExternal()方法 Exception in thread "main" java.io.OptionalDataExceptionat java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1349)at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)at test.serializable.Blip.readExternal(Blip.java:34)at java.io.ObjectInputStream.readExternalData(ObjectInputStream.java:1792)at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1751)at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)at test.serializable.ExternalizableTest.main(ExternalizableTest.java:28)? ? ? 原因是因?yàn)?#xff0c;我們在ObjectOutPutStream中沒有writeObject,而在ObjectInputStream中readObject導(dǎo)致的
?2)如果我們修改writeExternal()方法如下:
@Overridepublic void writeExternal(ObjectOutput out) throws IOException {System.out.println("調(diào)用writeExternal()方法");out.writeObject("自己定義的");out.writeInt(250);}那么運(yùn)行的結(jié)果為:
序列化之前 Blip帶參數(shù)構(gòu)造函數(shù) This String is 47 序列化操作,writeObject 調(diào)用writeExternal()方法 反序列化之后,readObject Blip默認(rèn)構(gòu)造函數(shù) 調(diào)用readExternal()方法 自己定義的250看見沒,反序列化后得到的s和i是我們在writeExternal()中自定義的數(shù)據(jù)
? 3) 如果我們只是修改readExternal()方法
@Overridepublic void readExternal(ObjectInput in) throws IOException,ClassNotFoundException {System.out.println("調(diào)用readExternal()方法"); // s = (String)in.readObject(); // i = in.readInt();}那么運(yùn)行的結(jié)果為:
序列化之前 Blip帶參數(shù)構(gòu)造函數(shù) This String is 47 序列化操作,writeObject 調(diào)用writeExternal()方法 反序列化之后,readObject Blip默認(rèn)構(gòu)造函數(shù) 調(diào)用readExternal()方法 null0? ?看見沒?最后一行打印的是null0,說明沒有對s和i進(jìn)行初始化。
4)如果我們刪除Blip的默認(rèn)構(gòu)造函數(shù),或者將其權(quán)限不設(shè)置為public
// public Blip() { // //默認(rèn)構(gòu)造函數(shù)必須有,而且必須是public // System.out.println("Blip默認(rèn)構(gòu)造函數(shù)"); // } // orBlip() {//默認(rèn)構(gòu)造函數(shù)必須有,而且必須是publicSystem.out.println("Blip默認(rèn)構(gòu)造函數(shù)");}運(yùn)行結(jié)果如下:
序列化之前 Blip帶參數(shù)構(gòu)造函數(shù) This String is 47 序列化操作,writeObject 調(diào)用writeExternal()方法 反序列化之后,readObject Exception in thread "main" java.io.InvalidClassException: test.serializable.Blip; test.serializable.Blip; no valid constructorat java.io.ObjectStreamClass.checkDeserialize(ObjectStreamClass.java:713)at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1733)at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1329)at java.io.ObjectInputStream.readObject(ObjectInputStream.java:351)at test.serializable.ExternalizableTest.main(ExternalizableTest.java:28) Caused by: java.io.InvalidClassException: test.serializable.Blip; no valid constructorat java.io.ObjectStreamClass.<init>(ObjectStreamClass.java:471)at java.io.ObjectStreamClass.lookup(ObjectStreamClass.java:310)at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1106)at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:326)at test.serializable.ExternalizableTest.main(ExternalizableTest.java:24)? 在反序列化時,會出現(xiàn)無效的構(gòu)造函數(shù)這個錯誤,可見必須有權(quán)限為public的默認(rèn)的構(gòu)造器(如果有非默認(rèn)的帶參數(shù)的構(gòu)造函數(shù),那么必須顯示的寫出默認(rèn)的構(gòu)造函數(shù),如果沒有非默認(rèn)的構(gòu)造函數(shù),那么默認(rèn)構(gòu)造函數(shù)可以不顯示的寫出來),才能使Externalizable對象產(chǎn)生正確的行為。
總結(jié)Externalizable對象的用法:
? ? 與Serizable對象不同,使用Externalizabled,就意味著沒有任何東西可以自動序列化,?為了正常的運(yùn)行,我們需要在writeExtenal()方法中將自對象的重要信息寫入,從而手動的完成序列化。對于一個Externalizabled對象,對象的默認(rèn)構(gòu)造函數(shù)都會被調(diào)用(包括哪些在定義時已經(jīng)初始化的字段),然后調(diào)用readExternal(),在此方法中必須手動的恢復(fù)數(shù)據(jù)。
《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的JAVA 对象序列化(二)——Externalizable的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: HTML5 本地文件操作之FileSys
- 下一篇: 详细讲解-sphinx配置文件