X

Java的Serializable接口

在写RPC调用的时候,难免会用到Serializable序列化,这个时候传输的对象就必须实现java.io.Serializable接口,这个接口里面没有方法也没有属性,只有一个长长的注释,我们看下这个注释都写了啥内容,总结如下

  1. 类要能被序列化必须实现Serializable的接口,没有实现这个接口的不能被序列化和反序列化,虽然这个接口既没有方法也没有属性,仅仅是语义上的一个声明.可能抛出NotSerializableException
  2. 如果父类实现了这个接口,那么他所有的子类都可以被序列化
  3. 如果子类实现了这个接口,而父类没有,那么为了让这个子类能被序列化,父类必须有一个子类能够访问到的无参构造函数.这样做的原因是为了让子类在反序列化构建的时候能正确的初始化他的父类。
  4. 如果想要自定义序列化和反序列化的过程,那么可以实现下面几个方法。
    writeObject 用来写入序列化数据
    readObject 用来恢复序列化数据
    readObjectNoData 用来读取特殊情况,比如发送方和接收方的类信息版本不一致或者一个继承了父类一个没有继承。或者stream信息不完整或者遭到破坏.
  5.  static 和 transient 修饰的字段会被忽略
  6.  serialVersionUID建议自定义,如果不自定义会使用默认的计算规则,而计算依赖于Java的编译实现,可能会导致发送方和接收方的不一致。不一致会抛出linkInvalidClassException异常。并且建议定义成private的
    private static final long serialVersionUID = 42L;
  7. 数组类没有serialVersionUID.

详细如下:

Serializability of a class is enabled by the class implementing the
java.io.Serializable interface.
序列化能力通过实现java.io.Serializable接口开启.
Classes that do not implement this interface will not have any of their state serialized or deserialized
没有实现这个接口的类将不能序列化或者反序列化
All subtypes of a serializable class are themselves 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.
要使得一个不可序列化的子类能够序列化,这个子类必须能保存他父类的public、protected和包级别的属性.
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 fields of non-serializable classes will be initialized using the public or protected no-arg constructor of the class.
在反序列化的时候, 不可序列化的父类的属性将通过无参的public或者protected的构造函数完成初始化
A no-arg constructor must be accessible to the subclass that is serializable.
这个无参的构造函数必须要能被可序列化的子类访问
The fields of serializable subclasses will be restored from the stream.
可序列化的子类的属性将从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
在这种情况下,将抛出一个NotSerializableException异常.
Classes that require special handling during the serialization and deserialization process must implement special methods with these exact signatures:
如果你的类在序列化和反序列化的时候需要一些特殊逻辑,那么你需要实现这几个特别的方法

private void writeObject(java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in) throws 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 it.
writeObjective方法负责写入这个对象的陈述,这样和他对应的readObject方法能够恢复他
The default mechanism for saving the Object's fields can be invoked by calling out.defaultWriteObject.
默认的情况, 可以通过调用out.defaultWriteObject方法来保存这个对象的属性域.
The method does not need to concern itself with the state belonging to its superclasses or subclasses.
这个方法不需要去关心这个陈述是属于他的子类还是父类
State is saved by writing the individual fields to the ObjectOutputStream using the writeObject method or by using the methods for primitive data types supported by DataOutput.
保存这个对象的陈述有两种方法。一种是通过writeObject方法将独立属性域写入ObjectOutputStream. 另外一种是通过DataOutput支持的原始数据类型.
The readObject method is responsible for reading from the stream and restoring the classes fields.
readObject方法则是用来从stream读取信息并且恢复这个类的.
It may call in.defaultReadObject to invoke the default mechanism for restoring the object's non-static and non-transient fields.
in.defaultReadObject来恢复没有被static 或者 transient关键字修饰的属性
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.
defaultReadObject方法使用保存在stream中的信息来赋值这个对象的相对应的属性字段
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 superclasses or subclasses.
这个方法也不需要关心描述信息是属于他的父类还是子类
State is saved by writing the individual fields to the ObjectOutputStream using the writeObject method or by using the methods for primitive data types supported by DataOutput.
保存这个对象的陈述有两种方法。一种是通过writeObject方法将独立属性域写入ObjectOutputStream. 另外一种是通过DataOutput支持的原始数据类型.
The readObjectNoData method is responsible for initializing 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.
当给出的类不是反序列化的父类的时候这种情况发生的时候, readObjectNoData的作用就是初始化这个对象的信息.
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 receiver's version extends classes that are not extended by the sender's version.
这通常发生在收到的部分使用的class和发送端的class的版本不同,或者接受到的类extend的类没有被发送端继承
This may also occur if the serialization stream has been tampered;
或者当收到的信息因为被干扰而不完整
This may also occur if the serialization stream has been tampered; hence, readObjectNoData is useful for initializing deserialized objects properly despite a "hostile" or incomplete source stream.
当收到的信息不完整或者被故意破坏的时候readObjectNoData就显得非常有用了.
Serializable classes that need to designate an alternative object to be used when writing an object to the stream should implement this special method with the exact signature:
如果可序列化的类在需要在写入stream的时候指派可选择的对象,那么他需要实现下面这个特殊的方法
ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
This readResolve method follows the same invocation rules and accessibility rules as writeReplace.
readResolve和writeReplace有相同的运行和访问规则
The serialization runtime associates with each serializable class a version number, called a serialVersionUID, 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.
运行时的序列化数据通过serialVersionUID和序列化的类进行关联. 这个id是用来在反序列化的时候验证发送和接受放的兼容行.
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 {@linkInvalidClassException}.
如果接收到的类和发送的类有不同的serialVersionUID,将会抛一个linkInvalidClassException异常.
serializable class can declare its own serialVersionUID explicitly by declaring a field named <code>"serialVersionUID"</code> that must be static, final, and of type <code>long</code>:<p>
你可以自定义这个serialVersionUID属性,他必须是名称是serialVersionUID并且是static 和 final修饰的. 他是一个long型的数据
例如 static final long serialVersionUID = 42L;
If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java(TM) Object Serialization Specification.
如果一个序列化的类没有定义serialVersionUID字段, 那么在运行阶段就会根据Java的类信息给自动生成一个.
However, it is <em>stronglyrecommended</em> that all serializable classes explicitly 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 <code>InvalidClassException</code>s during deserialization.InvalidClassException
然而还是强烈建议自定一个这个字段,因为他的计算过程非常依赖于class的信息信息,而不同的编译的实现会导致这些class的信息有差异,以至于计算出来的值在发送端和接收端不一致,从而导致InvalidClassException异常
Therefore, to guarantee a consistent serialVersionUID value across different java compiler implementations, a serializable class must declare an explicit serialVersionUID value.
所以为了在不同的Java编译实现能有相同的serialVersionUID,你最好能自己为类定义一个固定的值
It is also strongly advised that explicit serialVersionUID declarations use the <code>private</code> modifier where possible, since such declarations apply only to the immediately declaring
class--serialVersionUID fields are not useful as inherited members.
另外也建议将这个字段设置为private的权限, 因为这个声明仅仅用在类的声明上 -- 并不能作为一个集成的成员
Array 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.
数组类不能定义serialVersionUID字段,所以他们使用默认计算的值.但是在数组上的serialVersionUID的值匹配被放弃了.

 

 

Categories: Java学习
龙安_任天兵: 不忘初心,方得始终!