正常情況下,一個類實現java序列化很簡單,只需要implements Serializable接口即可,之後該類在跨jvm的傳輸過程中會遵照默認Java序列化規則序列化和反序列化;不同jvm版本之間序列化方式稍有不同,但基本上都是兼容的。
在某些特殊情況下,可能需要自定義序列化和反序列化的行為,看下面例子:
Java代碼
- class AbstractSerializeDemo {
- private int x, y;
- public void init(int x, int y) {
- this.x = x;
- this.y = y;
- }
- public int getX() {
- return x;
- }
- public int getY() {
- return y;
- }
- public void printXY() {
- System.out.println("x:" + x + ";y:" + y);
- }
- }
- public class SerializeDemo extends AbstractSerializeDemo implements Serializable {
- private int z;
- public SerializeDemo() {
- super.init(10, 50);
- z = 100;
- }
- public void printZ() {
- super.printXY();
- System.out.println("z:" + z);
- }
- public static void main(String[] args) throws IOException, ClassNotFoundException {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- ObjectOutputStream out = new ObjectOutputStream(bos);
- SerializeDemo sd = new SerializeDemo();
- sd.printZ();
- out.writeObject(sd);
- ObjectInputStream in = new ObjectInputStream(new ByteArrayInputStream(bos.toByteArray()));
- SerializeDemo sd2 = (SerializeDemo) in.readObject();
- sd2.printZ();
- }
- }
這段程序表示了一個可序列化的類繼承自一個非序列化的有狀態超類,期望的結果是,子類序列化以後傳輸並反序列化回來,原先的值域包括超類的值域都保持不變。
但是輸出是:
Java代碼
- x:10;y:50
- z:100
- x:0;y:0
- z:100
結果和期望不符,子類的值域保留下來了,但是超類的值域丟失了,這對jvm來說是正常的,因為超類不可序列化;
為了解決這個問題,只能自定義序列化行為,具體做法是在SerializeDemo裡加入以下代碼:
Java代碼
- private void writeObject(ObjectOutputStream os) throws IOException {
- os.defaultWriteObject();//Java對象序列化默認操作
- os.writeInt(getX());
- os.writeInt(getY());
- }
- private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException {
- is.defaultReadObject();//Java對象反序列化默認操作
- int x=is.readInt();
- int y=is.readInt();
- super.init(x,y);
- }
writeObject和readObject方法為JVM會在序列化和反序列化Java對象時會分別調用的兩個方法,修飾符都是private,沒錯。
我們在序列化的默認動作之後將超類裡的兩個值域x和y也寫入object流;與之對應在反序列化的默認操作之後讀入x和y兩個值,然後調用超類的初始化方法。
再次執行程序之後的輸出為:
Java代碼
- x:10;y:50
- z:100
- x:10;y:50
- z:100
另外還有兩個自定義序列化方法writeReplace和readResolve,分別用來在序列化之前替換序列化對象 和 在反序列化之後的對返回對象的處理。一般可以用來避免singleTon對象跨jvm序列化和反序列化時產生多個對象實例,事實上singleTon的對象一旦可序列化,它就不能保證singleTon了。JVM的Enum實現裡就是重寫了readResolve方法,由JVM保證Enum的值都是singleTon的,所以建議多使用Enum代替使用writeReplace和readResolve方法。
Java代碼
- private Object readResolve()
- {
- return INSTANCE;
- }
- private Object writeReplace(){
- return INSTANCE;
- }
注:writeReplace調用在writeObject前;readResolve調用在readObject之後。