# 序列化
序列化:Java中的序列化机制能够将一个实例对象信息写入到一个字节流中(只序列化对象的属性值,不序列化方法),序列化后的对象可用于网络传输,或者持久化到数据库、磁盘中。「将对象转化为字节流」
反序列化:需要对象的时候,再通过字节流中的信息来重构一个相同的对象。「将字节流转化为对象」
# 实现序列化
所有可在网络上传输的对象都必须是可序列化的,传入的参数或者返回的对象都是可序列化的,否则会出错;所有需要保存到磁盘的Java对象都必须是可序列化的,所以通常建议,程序创建的每个JavaBean类都去实现Serializable
接口。
Java要使一个类实现序列化,最简单的方式就是实现Serializable
接口的方式。一旦实现了此接口,该类的对象就是可序列化的。
除此之外,可以通过实现Externalizable
接口来实现序列化,必须实现writeExternal
、readExternal
方法。Externalizable
接口实际上是继承了Serializable
接口。
两种方法的对比是:
实现Serializable接口 | 实现Externalizable接口 |
---|---|
系统自动存储必要的信息 | 程序员决定要存储哪些信息 |
Java内建支持,易于实现,只需要实现该接口即可 | 必须实现接口的两个writerExternal 和readExternal 方法 |
性能略差 | 性能略好 |
# 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
来实现对象转二进制编码,而编码后的数组很大,影响存储和传输效率 - 序列化性能太差:耗时比较大