jdk8源码阅读之Serializable

Serializable

一个类可以通过实现java.io.Serializable接口获得串行化。不实现这个接口的类不会使任何状态序列化或反序列化。序列化类的所有子类型都是序列化的。序列化接口没有任何方法或属性,并且仅仅用于识别可序列化的语义。

这里写图片描述

为了非序列化类的子类型可以被序列化,子类型需要承担保存和恢复超类型的public,protected,和(如果可以访问的)package字段的责任。子类型要承担只有它继承的类具有可以访问的没有参数的构造方法初始化类的状态的责任。如果不是这样,声明一个类是Serializable是错误的。在运行时会检测到这个错误。

在反序列化时,非序列化类的字段会使用类的public或protected无参构造函数来初始化。序列化的子类必须能访问无参构造函数。序列化的子类的字段会从流中被恢复。

当遍历一张图表,一个对象也许会遇到不支持序列化的接口。在这个例子中,NotSerializableException会被抛出,和会识别不可序列化对象的类。

类在序列化过程需要特殊的处理和反序列化过程必须实现特别的方法以具有以下特征:

private void writeObject(java.io.ObjectOutputStream out) throws IOException

writeObject方法为特定的类写入对象的状态,使对应的readObject方法可以恢复它。默认机制是通过调出来调用保存对象的字段。defaultWriteObject.这个方法不需要关心属于它的超类或是子类的状态。使用writeObject方法或是使用DataOutput支持的原始数据类型写入各个字段到ObjectOutputStream来保存状态。

private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;

这个readObject方法是用来从流中读取和恢复类字段。defaultReadObject调用默认机制来恢复对象的非静态的和非瞬时的字段。这个defaultReadObject方法使用流中的信息来将保存在流中对象的字段分配给当前对象的相应命名的字段。当处理类进行添加新字段时,将处理这种情况。这个方法不需要关心属于它的超类或是子类的状态。使用writeObject方法或是使用DataOutput支持的原始数据类型方法将各个字段写入ObjectOutputStream来保存状态。

private void readObjectNoData() throws ObjectStreamException;

如果序列化流没有把给定的类作为被反序列化的对象的超类,则readObjectNoData方法负责初始化特定的类的对象的状态。这种情况出现在接收方使用了和发送方不同的反序列化实例的类的版本,并且接收方的版本拓展的类不能被发送方的版本拓展。这种情况也可能出现在序列化流被篡改。因此,尽管存在”敌对”或不完全的源流,readObjectNoData能用于正确地初始化反序列化的对象。

当写入对象到流中需要指定能用的可替代对象的序列化类要实现具有确切签名的特别的方法:

ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;

如果方法存在和能够通过在序列化的对象的类定义的方法来访问writeReplace()方法,可以通过序列化调用writeReplace方法。因此,这个方法可以有private,protected和package-private访问。子类访问这个方法遵循java可访问性原则。

从流中读取实例需要指定替换的类需要实现具有确切签名的特殊的方法。

ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;

这个readResolve方法遵循相同的调入原则和可访问性原则,类似writeReplace。

序列化运行时关系每个序列化的类一个版本号,叫做serialVersionUID,用来在反序列化期间识别序列化对象的发送方和接收方是否已经为该对象加载了和序列化兼容的类。如果接受方加载了一个和相对应的发送方的不同的serialVersionUID的对象的类,反序列化会出现InvalidException。一个序列化类可以通过字段”serialVersionUID”明确声明自己的serialVersionUID,这个字段必须是静态(statis),最终的(final)和long类型的。

ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;

如果序列化类没有明确声明一个serialVersionUID,这个序列化运行时会为那个类基于类的各个方面计算默认的serialVersionUID值,如Java(TM)对象序列化规范中的描述。然而,强烈推荐所有序列化的类明确声明serialVersionUID值,因为默认serialVersionUID的计算对类的细节很敏感,这可能因编译器的实现而已,和在反序列化时,可能会导致意想不到的InvalidClassException。因此,为了保证在不同Java编译器实现中有一个一致的serialVersionUID,一个序列化类必须明确声明一个serialVersionUID值。强烈建议明确声明serialVersionUID时,如果可能使用private修饰符,由于这样声明仅仅用于立即声明的类–作为继承成员的serialVersionID字段是无效的。数组类不能声明一个明确的serialVersionUID,所以它们有默认的计算值,但是数组类放弃匹配serialVeriosnUID值的要求。

以上是自己结合谷歌翻译做的笔记,建议大家去看看英文版的源码说明,会清晰很多。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值