# 序列化

序列化:Java中的序列化机制能够将一个实例对象信息写入到一个字节流中(只序列化对象的属性值,不序列化方法),序列化后的对象可用于网络传输,或者持久化到数据库、磁盘中。「将对象转化为字节流

反序列化:需要对象的时候,再通过字节流中的信息来重构一个相同的对象。「将字节流转化为对象

# 实现序列化

所有可在网络上传输的对象都必须是可序列化的,传入的参数或者返回的对象都是可序列化的,否则会出错;所有需要保存到磁盘的Java对象都必须是可序列化的,所以通常建议,程序创建的每个JavaBean类都去实现Serializable接口。

Java要使一个类实现序列化,最简单的方式就是实现Serializable接口的方式。一旦实现了此接口,该类的对象就是可序列化的。

除此之外,可以通过实现Externalizable接口来实现序列化,必须实现writeExternalreadExternal方法。Externalizable接口实际上是继承了Serializable接口。

两种方法的对比是:

实现Serializable接口 实现Externalizable接口
系统自动存储必要的信息 程序员决定要存储哪些信息
Java内建支持,易于实现,只需要实现该接口即可 必须实现接口的两个writerExternalreadExternal方法
性能略差 性能略好

# serialVersionUID

serialVersionUID是Java为每个序列化类产生的版本标识,它可以用来保证在反序列时,发送方和接收方接收的是可兼容的对象。即如果接收方接收的类的serialVersionUID与发送方发送的类的serialVersionUID不一致,那么就会抛出InvalidClassException异常。

假如可序列化的类并没有声明serivalVersionUID,那么运行时就会基于该类的各个方面计算出该类默认的serivalVersionUID的值。尽管如此,还是推荐在每一个序列化的类中显式指定serivalVersialUID的值。

serialVersion字段必须是static final long的类型。

# transient

在现实应用中,有些时候是不能使用默认序列化机制的。比如,有时候希望在序列化过程中,过滤、忽略掉敏感数据,或者简化序列化的流程,那么,就会使用到transient

当某个字段被声明为transient后,默认序列化机制就会忽略掉该字段的内容,该字段的内容在序列化后就会无法访问。如下例子所示:

public class SerializableDemo {
    static class Person implements Serializable {
        transient private Integer age = null;
    }
}

// Output:
// name: Jack, age: null, sex: MALE

可以看出,通过使用transient关键字声明age属性,age字段最终就没有被序列化。

# 缺陷

Java序列化同样存在不少的缺陷:

  • 无法跨语言:Java序列化目前只适用于Java语言实现的框架,其它语言大部分没有使用Java的序列化框架,也没有实现Java序列化的这套协议。
  • 容易被攻击
  • 序列化后的流很大:Java序列化使用了ObjectOutputStream来实现对象转二进制编码,而编码后的数组很大,影响存储和传输效率
  • 序列化性能太差:耗时比较大