Java 对象序列化

更多 Java 高级知识方面的文章,请参见文集《Java 高级知识》


Java 序列化

实现对象 Object 与字节 byte 的转换。例如在分布式环境中传递对象。

Java 序列化的实现

实现 Serializable 接口,该接口并不包含方法,更像是一个标记。

  • 通过 ObjectOutputStream 中的 writeObject() 方法将对象转换为字节序列。
    实际上操作的是一个对象图,包括该对象所引用的其他对象。
  • 通过 ObjectInputStream 中的 readObject() 方法将字节序列转换为对象。
    遍历对象图并逐个序列化。
    通过 readObject() 方法来创建对象时,不会调用其构造方法。

示例:

public class Serializable_Test {
    public static void main(String[] args) throws Exception {
        MyObj obj = new MyObj("ABC");

        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("myobj.txt"));
        oos.writeObject(obj);
        oos.flush();
        oos.close();

        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("myobj.txt"));
        MyObj obj2 = (MyObj) ois.readObject();
        ois.close();
        System.out.println(obj2.getName());
    }
}

class MyObj implements Serializable {
    private String name;

    public MyObj(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

以上的代码中,如果 MyObj 没有实现 Serializable 接口,会抛出异常 NotSerializableException

如果希望自定义对象序列化的实现方式:

  • 继承ObjectOutputStream 类,重写 writeObject() 方法
  • 继承ObjectInputStream 类,重写 readObject() 方法

哪些字段会被序列化

在默认的序列化实现中,会包括 非静态域非瞬时域,与域的可见性声明没有关系,可能导致隐私信息泄露。
如果不想让某个字段被序列号,可以使用 transient 关键字,例如将上述代码修改为:
private transient String name;
这样的话,obj2.getName() 会返回 null

关于序列化版本号 serialVersionUID

private static final long serialVersionUID = 1L;

  • 序列化版本号一致,向后兼容
  • 序列化版本号不一致,向后不兼容

例如在类的版本号为1时将对象写入到字节序列,随后将类的版本号改成2,再从字节序列中读取对象,会抛出异常:
java.io.InvalidClassException: advanced.MyObj; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2

你可能感兴趣的:(Java 对象序列化)