JDK 1.8 API阅读与翻译(3) Serializable

JDK 1.8 API阅读与翻译(3) Serializable

Author: Alex Wang

Date: March 3 2019


public interface Serializable

Serializability of a class is enabled by the class implementing the java.io.Serializable interface. Classes that do not implement this interface will not have any of their state serialized or deserialized. All subtypes of a serializable class are them serializable. The serialization interface has no methods or fields and serves only to identify the semantics of being serializable.

To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype's public, protected, and (if accessible) package fields. The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class's state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime.

During deserialization, the fileds of non-serializable classes will be initialized using the public or protected no-arg constructor of the class. A no-arg constructor must be accessible to the subclass that is serializable. The fields of serializable subclasses will be restored from the stream.

When traversing a graph, an object may be encountered that does not support the Serializable interface. In this case the NotSerializableException will be thrown and will identify the class of the non-serializable object.

Classes that require special handling during the serialization and deserialization process must implement special methods with these exact signature:

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

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

private void readObjectNoData() throws ObjectStreamException;

The writeObject method is responsible for writing the state of the object for its particular class so that the corresponding readObject method can restore if. The default mechanism for saving the Object's fields can be invoked by calling out. defaultWriteObject. The method does not need concern itself with the state belonging to its superclasses or subclasses. State is save by writing the individual fields to the ObjectOutputStream using the writeObject method or by using the methods for primitive data types supported by DataOutput.

The readObject method is responsible for reading from the stream and restoring the class fileds. It may call in.defaultReadObject to invoke the default merchanism for restoring the object's non-static and non-transient fields. The defaultReadObject method uses information in the stream to assign the fields of the object saved in the stream with the correspondingly named fields in the current object. This handles the case when the class has evolved to add new fields. The method does not need to concern itself with the state belonging to its superclass or subclasses. State is saved by wirting the individual fields to the ObjectOutputStream using the writeObject method or by using the medhods for primitive data types supported by DataOutput.

The readObjectNoData method is responsible for initializaing the state of the object for its particular class in the event that the serialization stream does not list the given class as a superclass of the object being deserialized. This may occur in cases where the receiving party uses a different version of the deserialized instance's class than the sending party, and the reveiver's version extends classes that are not extended by the sender's version. This may also occur if the serializatiopn stream has been tampered; hence, readObjectNoData is useful for initializaing deserialized objects properly despite a "hostile" or incomplete source stream.

Serializable classes that need to designate an alternative obect to be used when writing an object to the stream should implement this special method with the exact signature:

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

This writeReplace method is invoked by serialization if the method exists and it would be accessible from a method defined within the class of the object being serialized. Thus, the method can have privcate, protected and package-private acess. Subclass access when an instance of it is read from the stream should implement this special method with the exact signature.

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

This readResolve method follows the same invocation rules and accessiblility rules as writeReplace.

The serialization runtime associates with each serializable class a version number, called a seriaVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization. If the receiver has loaded a class for the object that has a different serialVersionUID than that of the corresponding sender's class, then deserialization will result in an InvalidClassException. A serializable class can declare its own serialVersionUID explicitly by declaring a field named "serialVersionUID" that must be static, final, and of type long:

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

If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculatue a default serialVersionUID value for that class based on various aspects of the class, as described in the Java Object Serialization Specification. However, it is strongly recommended that all serializable classes explicity declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations , and can thus result in unexpected InvalidClassExceptions during deserialization. Therefore, to guarantee a consistent serialVersionUID value accross different java compiler implementations, a serializable class must declare an explicit serialVersionUID value. It is also strongly advised that explicit serialVersionUID declaring class--serialVersionUID fields are not useful as inherited members. Arrary classes cannot declare an explicit serialVersionUID, so they always have the default computed value, but the requirement for matching serialVersionUID values is waived for array classes.

since

JDK1.1

See Also:

ObjectOutputStream, ObjectInputStream, ObjectOutput, ObjectInput, Externalizable


Java.io.Serializable接口的实现类启用了类的可序列化。没有实现这个接口的类并不会将任何状态序列化或者反序列化。可序列化勒的所有子类都是可以序列化的。序列化接口没有方法或者字段,仅仅用于标识可序列化的语义。

为了让非序列化类的子类可以被序列化,这个子类可以承担保存和恢复超类的pulic,protected,package字段(如果可访问的话)。只有当它拓展的类具有可访问无参构造函数来初始化类的状态时,子类才可以承担这样的责任。如果不是这种情况,那么就不能声明一个类是可序列化的。这个错误将在运行的时候被检测出来。

在反序列化区间,不可序列化的类中的字段会被pulblic或者protected的无参构造器初始化。必须对可序列化的子类访问无参构造器,可序列化的子类字段将从流中被恢复。

在遍历一个图时,可能会遇到不支持Serializable接口的情况。这种情况下,将会抛出NotSerializableException异常,并将这个类识别为不可序列化的对象。

在序列化和反序列化的过程中需要特殊处理的类必须要使用这些精确的声明来实现特殊的方法:

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

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

private void readObjectNoData() throws ObjectStreamException;

writeObject方法负责为其特定类编写对象的状态,以便相应的readObject方法可以恢复它。可以通过调用out.defaultWriteObject来调用保存的Object字段的默认机制。该方法不需要关注属于其超类或者自鳄梨的状态。通过使用writeObject方法或者使用DataOutput支持的原始数据类型的状态。通过使用writeObject方法或者使用DataOutput支持的原始数据类型的方法将各个字段写入ObjectOutputStream来保存状态。

readObject方法负责从流中读取并恢复类的字段。它可以调用.in.defaultReadObject来嗲用恢复对象的非静态类和非瞬态字段的默认机制。defaultReadObject方法使用流中的信息来制定流中保存的对象的字段以及当前对象中相应命名的字段。这处理了类在演变为添加新字段时的情况。该方法不需要关注其属于超类或者子类的状态。通过使用writeObject方法或者使用DataOutput支持的原始数据类型的方法将各个字段写入ObjectOutputStream来保存状态。

readObjectNoData方法负责在序列化流未将给定类列为要反序列化的对象的超类的情况下初始化其特定类的对象状态。如果接收方使用和发送方不同版本的反序列化实例的类,并且接受方的版本拓展了未由发送方拓展的类,就可能发生这种情况。如果序列化流已经被篡改,也可能发生这种状况;因此,尽管存在“恶意”的或者不完整的流,readObjectNoData仍可以用来初始化反序列化对象。

需要制定在将对象写入流时使用的备用对象的可序列化类应该使用确切的声明来实现这个特殊方法:

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

如果方法存在,则可以通过序列化调用此writeReplace方法,并且可以从要序列化的对象的类中定义的方法访问该方法。因此,该方法可以具有private,protected,package-private类型方法的访问。子类对方法的访问遵循java的访问规则。

从流中读取实例时需要制定替换的类应该使用精确的声明实现此特殊方法。

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

这个readResolve方法遵循和writeReplace同样的调用和访问规则。

序列化过程运行时将每个可序列化的类与版本号关联,这个版本号称为serialVersionUID,在反序列化期间使用该版本号来验证序列化对象的发送方和接收方是否加载与该序列化兼容的该对象的类。如果接受方加载了一个和发送方有着不同的serialVersionUID的类,那么反序列化过程会抛出一个InvalidClassException的异常。可序列化类可以通过声明为“serialVersionUID”的字段显示声明其自己的serialVersionUID,字段必须时static,final并且时long类型:

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

如果可序列化的类没有显示声明serialVersionUID,则序列化运行时将给予类的各个方面计算该类默认的serialVersionUID,Java桂发中对这个过程有所描述。但是,强烈建议所有可序列化类显示声明serialVersionUID,因为默认的serialVersionUID的计算对类的细节高度敏感,这些一节可能因为编译器不同而不同,因此在反序列化期间可导致意外的InvalidClassExceptions.因此,为了保证代码可以在不同的编译器下正常编译,应该显式声明serialVersionUID。强烈建议在声明serialVersionUID时使用private修饰符,因为此类声明仅适用于立即声明的class-serialVersion字段对继承的类并没有作用。数组类不能声明显式serialVersionUID,因此它们始终具有默认的计算值,但是对于数组类不需要匹配serialVersionUID值。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容