Java的对象序列化将那些实现了Serializable接口的对象转换成一个字节序列,
并能够在以后将这个字节序列完全恢复为原来的对象。这一过程甚至可通过网络进行,
自动弥补不同操作系统之间的差异
一是Java的远程方法调用RMI,使存活于其他计算机上的对象使用起来就像是存活于本机一样。
二是对Java Beans来说,对象的序列化也是必需的,使用一个Bean时,
在设计阶段对它的状态信息进行配置,并保存这些信息,在程序启动时进行后期恢复。
创建某些OutputStream对象,然后将其封装在一个ObjectOutputStream对象内,
调用writeObject()即可将对象序列化,并将其发送给OutputStream
创建某些InputStream对象,然后将其封装在一个ObjectInputStream对象内,
调用readObject()即可还原为对象,还原回来的对象是Object,需要类型转换
对象序列化时基于字节的,要使用字节输入流/输出流
对象序列化不仅保存了对象的“全景图”,而且能追踪对象内所包含的所有引用
/**
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 themselves
serializable. The serialization interface has no methods or fields
and serves only to identify the semantics of being serializable.
实现java.io.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.
为了允许序列化非可序列化类的子类型,子类型可以承担保存和恢复超类型的公共,
受保护和(如果可访问)包字段的状态的责任。
只有当它扩展的类具有可访问的no-arg构造函数来初始化类的状态时,子类型才可以承担此责任。
如果不是这种情况,则声明类Serializable是错误的。将在运行时检测到错误。
During deserialization, the fields 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.
在反序列化期间,将使用类的public或protected no-arg构造函数初始化非可序列化类的字段。
必须可以对可序列化的子类访问no-arg构造函数。可序列化子类的字段将从流中恢复。
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.
遍历图形时,可能会遇到不支持Serializable接口的对象。
在这种情况下,将抛出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. The default mechanism for saving
the Object's fields can be invoked by calling
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方法负责为其特定类编写对象的状态,
以便相应的readObject方法可以恢复它。
可以通过调用out.defaultWriteObject来调用保存Object字段的默认机制。
该方法不需要关注属于其超类或子类的状态。
通过使用writeObject方法或使用DataOutput
支持的原始数据类型的方法将各个字段写入ObjectOutputStream来保存状态。
The readObject method is responsible for reading from the stream and
restoring the classes fields. It may call in.defaultReadObject to invoke
the default mechanism 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 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.
readObject方法负责从流中读取并恢复类字段。
它可以调用in.defaultReadObject来调用恢复对象的非静态和非瞬态字段的默认机制。
defaultReadObject方法使用流中的信息来指定流中保存的对象的字段以及当前对象中相应命名的字段。
这处理了类在演变为添加新字段时的情况。
该方法不需要关注属于其超类或子类的状态。
通过使用writeObject方法或使用DataOutput支持的原始数据类型的方法
将各个字段写入ObjectOutputStream来保存状态。
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. 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. 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方法负责在序列化流未将给定类列
为要反序列化的对象的超类的情况下初始化其特定类的对象的状态。
如果接收方使用与发送方不同版本的反序列化实例的类,
并且接收方的版本扩展了未由发送方版本扩展的类,
则可能发生这种情况。如果序列化流已被篡改,
也可能发生这种情况;因此,尽管存在“恶意”或不完整的源流,
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:
需要指定在将对象写入流时使用的备用对象的可序列化类应该使用确切的签名实现此特殊方法:
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 private,
protected and package-private access. Subclass access to this method
follows java accessibility rules.
如果方法存在,则可以通过序列化调用此writeReplace方法,
并且可以从要序列化的对象的类中定义的方法访问该方法。
因此,该方法可以具有私有,受保护和包私有访问。
对此方法的子类访问遵循java可访问性规则。
Classes that need to designate a replacement when an instance of it
is read from the stream should implement this special method with the
exact signature.
从流中读取实例时需要指定替换的类应该使用精确签名实现此特殊方法。
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.
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 {@link 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
:
序列化运行时将每个可序列化类与版本号相关联,
称为serialVersionUID,在反序列化期间使用该版本号
来验证序列化对象的发送方和接收方是否已加载与该序列化兼容的该对象的类。
如果接收者为具有与相应发送者类的serialVersionUID不同的对象加载了一个类,
则反序列化将导致{@link InvalidClassException}。
可序列化类可以通过声明名为“serialVersionUID” code>
的字段来显式声明其自己的serialVersionUID,
该字段必须是static,final和 long code>类型:
ANY-ACCESS-MODIFIER 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. However, it is strongly
recommended 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
InvalidClassException
s during deserialization. Therefore, to
guarantee a consistent serialVersionUID value across different java compiler
implementations, a serializable class must declare an explicit
serialVersionUID value. It is also strongly advised that explicit
serialVersionUID declarations use the private
modifier where
possible, since such declarations apply only to the immediately declaring
class--serialVersionUID fields are not useful as inherited members. 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值,
如Java(TM)对象序列化规范中所述。但是,强烈建议 em>
所有可序列化类都显式声明serialVersionUID值,
因为默认的serialVersionUID计算对类详细信息高度敏感,
可能因编译器实现而异,因此可能导致意外的反序列化期间的InvalidClassException code>。
因此,为了保证跨不同java编译器实现的一致的serialVersionUID值,
可序列化类必须声明显式的serialVersionUID值。强烈建议显式serialVersionUID声明尽可能使用
private code>修饰符,因为此类声明仅适用于立即声明的类 - serialVersionUID字段
作为继承成员无用。数组类不能声明显式的serialVersionUID,
因此它们始终具有默认的计算值,但是对于数组类,不需要匹配serialVersionUID值。
@author unascribed
@see java.io.ObjectOutputStream
@see java.io.ObjectInputStream
@see java.io.ObjectOutput
@see java.io.ObjectInput
@see java.io.Externalizable
@since JDK1.1
*/
public interface Serializable {
}
a. 基础
@Test
public void test03() {
String f = "src/main/resources/file/a.txt";
try {
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(f));
Student student = new Student();
student.setName("aaa");
student.setAge(18);
objectOutputStream.writeObject(student);
} catch (IOException e) {
e.printStackTrace();
}
}
@Test
public void test04(){
String f = "src/main/resources/file/a.txt";
try {
ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream(f));
Student student = (Student) objectInputStream.readObject();
System.out.println(student);
}catch (Exception e){
e.printStackTrace();
}
//如果没有设置serialVersionUID的话,会抛出java.io.InvalidClassException 异常
}
b. 反序列化回来的对象和原对象内容一致,但是一个新对象
@Test
public void test05() throws IOException, ClassNotFoundException {
ObjectOutputStream objectOutputStream = null;
ObjectInputStream objectInputStream = null;
String f = "src/main/resources/file/a.txt";
Student student = new Student();
student.setName("aaa");
student.setAge(18);
try {
objectOutputStream = new ObjectOutputStream(new FileOutputStream(f));
objectOutputStream.writeObject(student);
objectInputStream = new ObjectInputStream(new FileInputStream(f));
} catch (IOException e) {
e.printStackTrace();
}
Student student2 = (Student) objectInputStream.readObject();
System.out.println(student == student2 ? "true" : "false");
}
若希望类的不同版本对序列化兼容,需要确保类的不同版本具有相同的serialVersionUID;
若不希望类的不同版本对序列化兼容,需要确保类的不同版本具有不同的serialVersionUID;
对象中存在引用对象时的序列化,其引用的对象也需要实现Serializable接口;
当父类实现了Serializable接口时,其子类不需要显示声明Serializable接口也可以序列化
当父类未实现了Serializable接口,但子类实现了Serizlizable接口,子类序列化时会失败
static 关键字修饰变量,不受序列化反序列化影响,序列化和反序列化都是基于对象,但是static是类的任何实例对象共享的
transient 关键字 修饰的变量,也不受序列化反序列化影响,该变量的值反序列化时是该变量数据类型的默认值,相当于隐藏了原来的值
目前只用到这些概念,先写到这,当然还有其他。
https://blog.csdn.net/rickesy/article/details/56677283