Home  >  Article  >  Java  >  Detailed explanation of object serialization and deserialization in Java

Detailed explanation of object serialization and deserialization in Java

高洛峰
高洛峰Original
2017-01-18 11:22:161419browse

The examples in this article describe object serialization and deserialization in Java. Share it with everyone for your reference. The details are as follows:

1. Introduction

Object serialization (Serializable) refers to the process of converting an object into a byte sequence, while deserialization is the process of restoring an object based on a byte sequence. .

Serialization is generally used in the following scenarios:

1. Save the object permanently and save the byte sequence of the object to a local file;
2. Serialize the object in the network Pass objects;
3. Pass objects between processes through serialization.

The class to which the object belongs must implement the Serializable or Externalizable interface in order to be serialized. For classes that implement the Serializable interface, the default serialization method is used for serialization and deserialization. The Externalizable interface is an interface that inherits the Serializable interface and is an extension of Serializable. Classes that implement the Externalizable interface completely control the serialization and deserialization by themselves. Deserialization behavior.

Java.io.ObjectOutputStream represents the object output stream. Its method writeObject(Object obj) can realize the serialization of the object and write the obtained byte sequence to the target output stream.

Java.io.ObjectInputStream represents an object input stream. Its readObject() method can read a byte sequence from the source input stream, deserialize it into an object, and return it.

2. Several ways of serialization

Assume that a Customer class is defined. Depending on the way Customer implements serialization, there may be the following serialization methods:

1. Implement Serializable, undefined readObject and writeObject methods

ObjectOutputStream uses the JDK default method to serialize the non-transient instance variables of the Customer object;
ObjectInputStream uses the JDK default method to serialize the non-transient instance variables of the Customer object The instance variables are deserialized.

2. Implement Serializable and define the readObject and writeObject methods

ObjectOutputStream calls the writeObject (ObjectOutputStream out) method of the Customer class to serialize the non-transient instance variables of the Customer object;
ObjectInputStream calls the readObject(ObjectInputStream in) method of the Customer class to deserialize the non-transient instance variables of the Customer object.

3. Implement Externalizable and define the readExternal and writeExternal methods

ObjectOutputStream calls the writeExternal method of the Customer class to serialize the non-transient instance variables of the Customer object;
ObjectInputStream first passes the Customer class's writeExternal method The parameterless constructor instantiates an object, and then uses the readExternal method to deserialize the non-transient instance variables of the Customer object.

3. Serializable interface

The class implements the java.io.Serializable interface to enable its serialization function. A class that does not implement this interface will not be able to serialize or deserialize any of its state. All subtypes of a serializable class are themselves serializable. The serialization interface has no methods or fields and is used only to identify serializable semantics.

During the deserialization process, fields of non-serializable classes are initialized using the class's public or protected parameterless constructor methods. Serializable subclasses must have access to the parameterless constructor. Fields of serializable subclasses will be restored from this stream.

When traversing a class view, you may encounter objects that do not support the Serializable interface. In this case, NotSerializableException will be thrown and the class of the non-serializable object will be identified.

1. Accurate signature

Classes that require special handling during serialization and deserialization must use the following accurate signature to implement special methods:

private void writeObject( java.io.ObjectOutputStream out) throws IOException
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException;
private void readObjectNoData() throws ObjectStreamException;

writeObject method is responsible for writing The state of an object of a specific class so that the corresponding readObject method can restore it. The default mechanism for saving an Object's fields can be invoked by calling out.defaultWriteObject. The method itself need not involve state belonging to its superclass or subclass. State can be saved by writing individual fields to an ObjectOutputStream using the writeObject method or using the methods supported by DataOutput for primitive data types.

The readObject method is responsible for reading and restoring class fields from the stream. It can call in.defaultReadObject to invoke the default mechanism for restoring the object's non-static and non-transient fields. The defaultReadObject method uses information from the stream to allocate the fields of the object in the stream held by the corresponding specified fields in the current object. This is used to handle situations where new fields need to be added after the class evolves. The method itself need not involve state belonging to its superclass or subclass. State can be saved by writing individual fields to an ObjectOutputStream using the writeObject method or using the methods supported by DataOutput for primitive data types.

The readObjectNoData method is responsible for initializing the object state of a specific class in the case where the serialization stream does not list the given class as a superclass of the object to be deserialized. This occurs when the receiver uses a different version of the deserialized instance class than the sender, and the receiver's version extends a class that is not extended by the sender's version. It will also occur when the serialization stream has been tampered with; therefore, the readObjectNoData method can be used to correctly initialize the deserialized object regardless of whether the source stream is "hostile" or incomplete.

When writing an object to a stream you need to specify the serializable class of the replacement object to use. This special method should be implemented with the exact signature:

ANY-ACCESS-MODIFIER Object writeReplace( ) throws ObjectStreamException;
This writeReplace method will be called by serialization if this method exists and it can be accessed through a method defined in the class of the serialized object. Therefore, the method can have private, protected, and package-private access. Access to this method by subclasses follows Java access rules.

When reading an instance of a class from the stream you need to specify the exact signature that the alternative class should use to implement this special method.

ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException;
This readResolve method follows the same calling rules and access rules as writeReplace.
If a class defines the readResolve method, the readResolve method will be called at the end of deserialization, and the object returned by this method is the final result of deserialization.

2.serialVersionUID

The serialization runtime uses a version number called serialVersionUID to associate with each serializable class, which is used to verify the sequence during the deserialization process Whether the sender and receiver of a serialization object have loaded a serialization-compatible class for the object. If the serialVersionUID of the object's class loaded by the receiver is different from the corresponding version number of the sender's class, deserialization will result in an InvalidClassException. A serializable class can explicitly declare its own serialVersionUID by declaring a field named "serialVersionUID" (which must be a static, final long field):
ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
If a serializable class does not explicitly declare a serialVersionUID, the serialization runtime will calculate a default serialVersionUID value for the class based on aspects of the class, as specified in the "Java(TM) Object Serialization Specification" ” stated in. However, it is strongly recommended that all serializable classes explicitly declare a serialVersionUID value because calculating the default serialVersionUID is highly sensitive to the details of the class and may vary widely depending on the compiler implementation, so that during the deserialization process May cause unexpected InvalidClassException. Therefore, to ensure consistency of serialVersionUID values ​​across different Java compiler implementations, serialization classes must declare an explicit serialVersionUID value. It is also strongly recommended to explicitly declare serialVersionUID using the private modifier (if possible), the reason being that such a declaration should only be used in directly declaring classes -- the serialVersionUID field is of no use as an inherited member. Array classes cannot declare an explicit serialVersionUID, so they always have a default calculated value, but there is no requirement for array classes to match the serialVersionUID value.

3.Externalizable interface

Externalizable is an extension of Serailizable. The serialization of a class that implements the Externalizable interface has the following characteristics:
The method writeExternal of the class is called during serialization and called during deserialization. readExternal method;
When performing deserialization, the parameterless constructor of the class is first called. This is different from the default deserialization. Therefore, for classes that implement the Externalizable interface to implement serialization, a class must be provided. public parameterless constructor, otherwise an exception will occur during deserialization.

4. Summary

If the default serialization method is used, as long as a class implements the Serializable interface, its instances can be serialized. Generally, classes designed specifically for inheritance should try not to implement the Serializable interface, because once the parent class implements the Serializable interface, all its subclasses are also serializable.

Disadvantages of the default serialization method:

1. It is unsafe to directly serialize sensitive data of objects that are not suitable for public disclosure;
2. It will check whether the member variables of the object meet the correct constraints, and the data may be tampered with, causing abnormal operation;
3. It is necessary to recursively traverse the object graph. If the object graph is very complex, it will consume a lot of resources, and the setting will cause Java Stack overflow of the virtual machine;
4. Make the interface of the class constrained by the internal implementation of the class, restricting the upgrade and maintenance of the class.

By implementing the private type writeObject() and readObject() of the Serializable interface, or implementing the Externalizable interface, implementing the writeExternal() and readExternal() methods, and providing two public type parameterless constructors. Method to control the serialization process can effectively avoid the shortcomings of the default serialization method.

I hope this article will be helpful to everyone’s Java programming.

For more detailed articles on object serialization and deserialization in Java, please pay attention to the PHP Chinese website!

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn