To put it simply, Java's serialization mechanism verifies version consistency by judging the serialversionuid of the class at runtime. During deserialization, the JVM compares the serialversionuid In the byte stream with the serialversionuid of the corresponding local entity (class). If they are the same, they can be deserialized, otherwise, the serialization version is inconsistent. When Java. io. when the entity (class) of the serializable interface does not explicitly define a variable named serialversionuid whose type is long, the Java serialization mechanism will automatically generate a serialversionuid Based on the compiled class for the serialization version comparison. In this case, only the class generated by the same compilation will generate the same serialversionuid. If we do not want to forcibly divide the software version by compiling, that is, the entity implementing the serialization interface can be compatible with the previous version. If the class is not changed, we need to explicitly define a class named serialversionuid, variables of the long type can be serialized or deserialized to each other without modifying the serialization entity of the variable value. Serialversionuid is used to determine the compatibility of different versions. During deserialization, the version number in the byte stream is compared with the serialversionuid area to generate its object. If you do not set the Java serialization mechanism, it will automatically allocate one to you. In fact, this thing does not need to be considered!
The public interface serializable class enables its serialization function by implementing the java. Io. serializable interface. Classes that do not implement this interface cannot be serialized or deserialized in any State. All sub-types of serializable classes are themselves serializable. The serialization interface has no methods or fields and is only used to identify the serializable semantics.
To allow serialization of subtypes of non-serializable classes, assume that this subtype is responsible for saving and recovering ultra-type public, protected, and (if accessible) the status of the package field. Only when the child type extension class has an accessible non-parameter constructor to initialize the state of the class can it be assumed that the child type has this responsibility. If this is not the case, it is wrong to declare a class as a serializable class. This error will be detected at runtime.
During deserialization, fields of the non-serializable class will be initialized using the public or protected non-parameter constructor of this class. The serializable subclass must be able to access the non-parameter constructor. The field of the serializable subclass will be restored from the stream.
When traversing a graph, you may encounter objects that do not support the serializable interface. In this case, notserializableexception will be thrown and classes that identify non-serializable objects will be thrown.
Classes that require special processing during serialization and deserialization must use the following accurate signatures 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;
The writeobject method is used to write the state of the object of a specific class so that the corresponding readobject method can restore it. You can call out. defaultwriteobject to call the default mechanism for saving the fields of the object. This method does not need to involve the status of its superclass or subclasses. You can write fields to objectoutputstream by using the writeobject method or the method supported by dataoutput for basic data types. The status can be saved.
The readobject method reads and restores class fields from the stream. It can call in. defaultreadobject to call the default mechanism to restore non-static and non-transient fields of the object. The defaultreadobject method uses the information in the stream to allocate the fields of the object saved by the corresponding specified field in the current object in the stream. This is used to process the situation where new fields need to be added after class evolution. This method does not need to involve the status of its superclass or subclasses. You can write fields to objectoutputstream by using the writeobject method or the method supported by dataoutput for basic data types. The status can be saved.
The readobjectnodata method initializes the object status of a specific class without listing a given class as the superclass of the deserialized object. This occurs when the version of the deserialization instance class used by the receiver is different from that of the sender, and the class of the receiver version extension is not the class of the sender version extension. This happens when the serialized 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 must specify the serializable class of the alternative object to be used. Use an accurate signature to implement this special method:
Any-access-modifier object writereplace () throws objectstreamexception;
This writereplace method will be called by serialization, provided that this method exists and can be accessed through a method defined in the class of the serialized object. Therefore, this method can have private, protected, and package-Private access. The subclass follows the Java access rules for this method.
When reading an instance of a class from a stream, you must specify the correct signature for the substitute Class 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.
During serialization, a version number called serialversionuid is used to associate with each serializable class, in the deserialization process, this serial number is used to verify whether the sender and receiver of the serialized object have loaded classes compatible with serialization for this object. If the serialversionuid of the class loaded by the receiver is different from the version of the class of the corresponding sender, deserialization will lead to invalidclassexception. The serializable class can explicitly declare its own serialversionuid by declaring a field named "serialversionuid" (which must be a static and final long field:
Any-access-modifier static final long serialversionuid = 42l;
If the serialversionuid is not explicitly declared for the serializable class, the default serialversionuid value of the class is calculated based on all aspects of the class during the serialization runtime, as described in "Java (TM) Object serialization specification. However, it is strongly recommended that all serializable classes explicitly declare the serialversionuid value, because calculating the default serialversionuid is highly sensitive to the class details, which may vary according to compiler implementations, in this way, unexpected invalidclassexception may occur during deserialization. Therefore, to ensure the consistency of serialversionuid values across different Java compilers, the serialization class must declare a clear serialversionuid value. We strongly recommend that you use the private modifier to display the Declaration serialversionuid (if possible), because this declaration is only applicable to the direct Declaration class -- serialversionuid field as an inherited member. The array class cannot declare a clear serialversionuid, so they always have the default calculated value, but the array class does not match the serialversionuid value.