concept

Serialization is the process of converting an object’s state information into a form that can be stored or transmitted. During serialization, an object writes its current state to a temporary or persistent store. Later, you can recreate the object by reading or deserializing its state from the store.

In a nutshell:

  • Serialization: The process of converting a Java object to bytecode.
  • Deserialization: The process of converting a sequence of bytes into Java objects.

Why serialize/deserialize?

We send all kinds of data over the network, including text, pictures, audio, video, and so on, all of which are transmitted over the network as binary sequences. So how do two Java processes transfer objects when they communicate? That’s where serialization/deserialization comes in. The sender serializes the Java object for transmission over the network, and the receiver returns the byte sequence to the Java object. Perfect communication between two Java processes.

Simply put: Make custom objects persistent in some form of storage so that objects can be passed from one place to another. Make your application more maintainable.

Serialization/deserialization concrete implementation

For example, we serialize a class into a local file and deserialize the file into a Java object.

// Define a Java object
public class Student implements Serializable { 
    private String name; private Integer age; 
    private Integer score; 
    
    @Override public String toString(a) { 
        return "Student:" + '\n' + "name = " + this.name + '\n' + "age = " + this.age + '\n' + "score = " + this.score + '\n'; 
    } 
    
    / /... Getter setter slightly...
}
Copy the code
/ / the serialization
public static void serialize(a) throws IOException {
   Student student = new Student();
   student.setName("feiyangyang");
   student.setAge(18);
   student.setScore(100);

   ObjectOutputStream objectOutputStream =
           new ObjectOutputStream(new FileOutputStream(new File("student.txt")));
   objectOutputStream.writeObject(student);
   objectOutputStream.close();

   System.out.println("Serialization successful! The student.txt file has been generated.);
   System.out.println("= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =");
}
Copy the code
// deserialize
public static void deserialize(a) throws IOException, ClassNotFoundException {
    ObjectInputStream objectInputStream = 
        new ObjectInputStream( new FileInputStream( new File("student.txt"))); Student student = (Student) objectInputStream.readObject(); objectInputStream.close(); System.out.println("Deserialization results in:");
    System.out.println(student);
}
Copy the code

Output result:

Serialization successful! Have been generated student. TXT file = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = deserialization results as follows: student: name = feiyangyang age = 18 score = 100Copy the code

In the simple example above, a serialization/deserialization is completed.

What is Serializable for?

In this example, the Student class implements the Serializable interface. The Serializable interface is null. What’s the use of an empty interface? Let’s work backwards and let Student not implement the Serializable interface and see what happens.

Exception in thread "main" java.io.NotSerializableException: com.dmsd.serializable.Student
	at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1185)
	at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:349)
	at com.dmsd.serializable.Serialization.main(Serialization.java:23)
Copy the code

NotSerializableException is thrown directly.

If the object is a String, array, enumeration, or Serializable interface, throw NotSerializableException. If the object is a String, array, or enumeration, throw NotSerializableException

Originally, the Serializable interface was implemented only to indicate whether it can be serialized, not to implement the serialization action.

Serialize the role of ID

If you’ve ever seen a variable like this, what’s the use of declaring a serialVersionUID like this?

private static final long serialVersionUID = -4392658638228508589L;
Copy the code

Continuing with the example above, the Student class does not explicitly declare a serialVersionUID field, but still does not affect serialization.

What if we add a little twist? Add a property to the Student class.

public class Student {
	private String id;
}
Copy the code

Let’s take the TXT file that has just been serialized and try to deserialize it to see if it can succeed.



InvalidClassException: serialVersionUID number is not compatible!

SerialVersionUID is not compatible with serialVersionUID before and after serialization.

Two conclusions can be drawn from the exception information:

  • SerialVersionUID is the unique identifier before and after serialization
  • If there is no explicit definition of serialVersionUID, the compiler automatically declares one

Since serialVersionUID before and after serialization is not compatible, it indicates that THE JVM will compare the serialVersionUID before and after serialization. If the serialVersionUID before and after serialization is consistent, the serialization will succeed. Otherwise, the deserialization process will be terminated abnormally.

If serialVersionUID is not explicitly defined, the compiler automatically generates one, and once the structure or information of the class is changed, a serialVersionUID is generated again for the class

Since there is no guarantee that a class’s structure is permanent, it is best to explicitly define a serialVersionUID for all implements Serializable classes

You can easily use IDEA to automatically generate and add the serialVersionUID field to your class. (See the end of this article for how to generate three links with one key)

Can attributes in a serializable class be serialized/deserialized as long as they are implemented?

Static modifiers will not be serialized, because serialization stores the state of the object, not the state of the class. Transient variables will not be instantiated

If you want a field not to be serialized when serializing a class, you can use transient modifier fields.

Let’s add a password variable to Student and try reserializing/deserializing.

private transient String password;
Copy the code
Student{name=feiyangyang age=18 score=100 password=null}Copy the code

The value of password is null, indicating that the password has not been serialized.

In the process of network transmission, data will inevitably have security problems. If serialized byte streams are sent over the network, there is a risk of being forged or tampered with, and the contents of the antisequence cannot be guaranteed to be correct. After all, antisequences are “implicit” object constructs, and by reading byte streams to construct them, we can add some constraints to the serialized data.

Constraints on deserialization

To address the above problem, we can write our own readObject function for deserialization construction of the object to provide constraints.

For example, the score value in the Student class is expected to be between 1 and 100. In order to prevent score from being forged in the process of deserialization, we write our own readObject() function for deserialization control.

public class Student implements Serializable {
	private void readObject( ObjectInputStream objectInputStream ) throws IOException, ClassNotFoundException {

    <span class="token comment">// Call default deserialization function 
    objectInputStream<span class="token punctuation">.</span><span class="token function">defaultReadObject</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">; </span> <spanclass="token comment">// Manually check the validity of the result after deserialization, if there is a problem, terminate the operation! 
    <span class="token keyword">if</span><span class="token punctuation">(</span> <span class="token number">0</span> <span class="token operator">&gt; </span> score <spanclass="token operator">||</span> <span class="token number">100</span> <span class="token operator">&lt; </span> score <spanclass="token punctuation">)</span> <span class="token punctuation"> {<! -- --></span> <spanclass="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">IllegalArgumentException</span><span class="token punctuation">(</span><span class="token string">"Students can only score between 0 and 100!"</span><span class="token punctuation">)</span><span class="token punctuation">; </span> <spanclass="token punctuation">}</span>
<span class="token punctuation">}</span>




}


Copy the code

I set score to > 100, and the anti-sequence operation is immediately terminated with an error.

How can a private method we define in a class be called automatically? We explore the underlying source code to understand.



Everything in Java can be reflected. By reflection, no matter how private you are, you can figure it out. That’s the power of reflection.

conclusion

Java objects need to be serialized/deserialized whenever they are persisted. If we persist data to the database, is this process also serialized?

Yes, the storage part of a database is essentially a serialized storage of data structures. Database storage and other file format storage has no essential difference, are content index (format description, read and write instructions) + specific data.



Alt + Enter on the class and you’re done! Don’t forget to generate a serialVersionUID for your class.