最近项目中要用到Serializable,写个总结免得自己忘记。

Serializable 是 Java 所提供的一个序列化接口, 它是一个空接口, 为对象提供标准的序列化和反序列化操作,使用时,类只需要实现该接口,并提供一个序列化接口serialVersionUID 即可。

创建一个 MySerializable 实现 Serializable, 并不需要实现任何接口,正常使用这个类即可。

public class MySerializable implements Serializable {
    static final long serialVersionUID = 6201933920870624595L; //很重要
    private int t1;
    private int t2;
    private String s1;

    public int getT1() {
        return t1;
    }
    public void setT1(int t1) {
        this.t1 = t1;
    }
    public int getT2() {
        return t2;
    }
    public void setT2(int t2) {
        this.t2 = t2;
    }
    public String getS1() {
        return s1;
    }
    public void setS1(String s1) {
        this.s1 = s1;
    }
    @Override
    public String toString() {
        return super.toString() + " getS1 = " + getS1() + " getT1 = " + getT1() + " getT2 = " + getT2();
    }
}

在使用时,通常是和ObjectOutputStream 以及 ObjectInputStream 配套一起使用,准确的说是和 ObjectOutputStream 里的writeObject ()ObjectInputStream 里的 readObject () 一起使用。writeObject()方法是最重要的方法,用于对象序列化。如果对象包含其他对象的引用,则writeObject()方法递归序列化这些对象。

先看一段例子:

private void testMySerializable() {
    MySerializable mySerializable = new MySerializable();
    mySerializable.setS1("a");
    mySerializable.setT1(1);
    mySerializable.setT2(2);
    Log.d("MainActivity","before " + mySerializable.toString());
    MySerializable mySerializable1 = null;

    try {
        FileOutputStream stream = new FileOutputStream(new File(getFilesDir(), "a.txt"), true);

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(stream);

        objectOutputStream.writeObject(mySerializable);

        FileInputStream stream1 = new FileInputStream(new File(getFilesDir(), "a.txt"));
        ObjectInputStream inputStream = new ObjectInputStream(stream1);

        mySerializable1 = (MySerializable) inputStream.readObject();

        Log.d("MainActivity","after " + mySerializable1.toString());
    } catch (FileNotFoundException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    } catch (ClassNotFoundException e) {
        e.printStackTrace();
    }

}

结果

01-15 23:31:37.674 6536-6536/com.example.dongyayun.androidartsearch D/MainActivity: before com.example.dongyayun.androidartsearch.MySerializable@acedd12 getS1 = a getT1 = 1 getT2 = 2
01-15 23:31:37.676 6536-6536/com.example.dongyayun.androidartsearch D/MainActivity: after com.example.dongyayun.androidartsearch.MySerializable@839b85e getS1 = a getT1 = 1 getT2 = 2

如果我们把 static final long serialVersionUID = 6201933920870624595L; //很重要 这行删掉会有什么结果呢,一起来看看。 OK, 还是可以成功反序列化。

01-15 23:52:50.520 4442-4442/com.example.dongyayun.androidartsearch D/MainActivity: before com.example.dongyayun.androidartsearch.MySerializable@5addf14 getS1 = a getT1 = 1 getT2 = 2
01-15 23:52:50.529 4442-4442/com.example.dongyayun.androidartsearch D/MainActivity: after com.example.dongyayun.androidartsearch.MySerializable@9e28c37 getS1 = a getT1 = 1 getT2 = 2

接下来我们给MySerializable 增加一个成员, private String s2;

java.io.InvalidClassException: MySerializable: static final long serialVersionUID =-295707994301229867L; but expected com.example.dongyayun.androidartsearch.MySerializable: static final long serialVersionUID =-5421863751443195198L;

这时反序列化发生了错误,异常提示为两个类的serialVersionUID 不一致!
Effective Java 第二版第11章说到

UID(serial version UID) 这个自动产生的值会收到类名称,它所实现的接口的名称,以及所有公有的和受保护的成员的名称所影响。如果你通过任何的方式改变了这些信息,比如,增加了一个不是很重要的工具方法,自动产生的序列版本UID也会发生变化。因此,如果你没有生命一个显示的序列版本UID,兼容性将会遭到破坏,在运行时回导致java.io.InvalidClassException 异常。

在上述的例子中,我们在没有改变类的属性时,是可以成功反序列化成功,但是如果增加了成员,就会出现异常了。

所以在使用Serializable 时一定要显式的制定一个UID ! 有兴趣的同学可以在尝试一种场景,UID指定的情况下,删除一个已经序列化的类的成员变量,然后看看是否可以反序列化成功~

另外还需要说明的是,如果一个类的成员声明为transient 或 static,那么这些成员将被初始化为他们的默认值;对于对象应用域,默认值为null,对于数组基本域,默认值为0,对于boolean域,默认值为false。

当然我们也并不是没有办法序列化transient 成员,例如这个类我们添加了一个 private transient int t3static int t4

package com.example.dongyayun.androidartsearch;

import java.io.IOException;
import java.io.Serializable;

/**
 * Created by dongyayun on 16/1/15.
 */
public class MySerializable implements Serializable {
    static final long serialVersionUID = 6201933920870624595L; //很重要
    static int t4;
    private int t1;
    private int t2;
    private transient int t3;
    private String s1;

    public static int getT4() {
        return t4;
    }

    public static void setT4(int t4) {
        MySerializable.t4 = t4;
    }

    public int getT3() {
        return t3;
    }

    public void setT3(int t3) {
        this.t3 = t3;
    }

    public int getT1() {
        return t1;
    }

    public void setT1(int t1) {
        this.t1 = t1;
    }

    public int getT2() {
        return t2;
    }

    public void setT2(int t2) {
        this.t2 = t2;
    }

    public String getS1() {
        return s1;
    }

    public void setS1(String s1) {
        this.s1 = s1;
    }

    @Override
    public String toString() {
        return super.toString() + " getS1 = " + getS1() + " getT1 = " + getT1() + " getT2 = " + getT2() + " getT3() =  " + getT3() + " getT4() =  " + getT4();
    }


    private void writeObject(java.io.ObjectOutputStream out)
            throws IOException {
        // write 'this' to 'out'...

        //只序列化 transient 成员t3
        out.writeInt(t3);
    }

    private void readObject(java.io.ObjectInputStream in)
            throws IOException, ClassNotFoundException {
        // populate the fields of 'this' from the data in 'in'...
        t3 = in.readInt();
    }
}

在类里,我们重写 writeObject readObject 方法, 这里我们只序列化t3,执行程序结果:

01-16 00:43:50.461  getS1 = a getT1 = 1 getT2 = 2 getT3() =  3 getT4() =  4
01-16 00:43:50.467  getS1 = null getT1 = 0 getT2 = 0 getT3() =  3 getT4() =  4

看到这个结果可能就有人要问了,static成员不也是不能序列化吗,为什么还能获取到值呢。 其实这里t4的值并不是反序列化的来的,而是当前JVM中对应static变量的值,这个值是JVM中的不是反序列化得出的,有兴趣的同学可以动手实验一把,在反序列化前将该值改变一下,然后打印~~~

对于writeObject readObject 可以直接调用默认的write和read方法来序列化该类的默认成员.

private void writeObject(java.io.ObjectOutputStream out)
        throws IOException {
    // write 'this' to 'out'...
    out.defaultWriteObject();
    out.writeInt(t3);
}

private void readObject(java.io.ObjectInputStream in)
        throws IOException, ClassNotFoundException {
    // populate the fields of 'this' from the data in 'in'...
    in.defaultReadObject();
    t3 = in.readInt();
}

Serializable 我们要序列化某个类,其实并不用将这个类implementate Serializable,还有另外一种办法~~~ 今天暂时就写到这里吧,后续再完善。