Github open Source project: github.com/hansonwang9… Has been included, there are detailed self-study programming learning routes, interview questions and interviews, programming information and a series of technical articles, resources continue to update…


Tools are

There was a time when serialization in Java was just “implement a Serializbale interface”, until…

So I took the time to revisit the “Ideas of Java Programming”, just as I had done with “Enumeration Parts”, and to revisit the “serialization and deserialization” part.


What does serialization do?

Serialization is intended to transform a Java object into a sequence of bytes, which is convenient for persistent storage to disk and prevents the object from disappearing from memory after the program runs. In addition, it is easier to transport and propagate the Java object into a sequence of bytes.

  • Serialization: Converts A Java object into a sequence of bytes.
  • Deserialization: Restores the byte sequence to the original Java object.

The serialization mechanism also makes up for some of the platformization differences in a sense, since the converted byte stream can be deserialized on other platforms to recover objects.

That’s the thing. It looks simple, but there’s more to come. Please read on.


How is an object serialized?

However, Java does not currently have a keyword that directly defines a so-called “persistent” object.

Object persistence and de-persistence require the programmer to manually and explicitly serialize and de-serialize the object in the code.

For example, if we want to serialize the Student class object to a text file named student.txt, and then deserialize the Student class object from the text file:

1. Student class definition

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'
        ;
    }
    
    / /... Other omissions...
}
Copy the code

2. Serialization

public static void serialize(a) throws IOException {

    Student student = new Student();
    student.setName("CodeSheep");
    student.setAge( 18 );
    student.setScore( 1000 );

    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

deserialization

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

4. Running results

Console printing:

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

What is the use of Serializable interface?

The Serializable interface is null and does not contain any methods.

Imagine what would happen if you forgot to add implements Serializable while defining the Student class above.

The result is that the program runs with an error and throws NotSerializableException:

WriteObject0 (), ObjectOutputStream, writeObject0(), ObjectOutputStream

If an object is neither a string, array, enumeration, nor implements the Serializable interface, a NotSerializableException is thrown on serialization!

Oh, I see!

Serializable interface is just a marker!!

It tells the code that any class that implements the Serializable interface can be serialized! The real serialization, however, doesn’t need it.


serialVersionUIDWhat’s the use of the number?

You’ll often see classes with a line of code that defines a field named serialVersionUID:

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

Do you know the meaning of this statement? Why do we have a program calledserialVersionUIDSerial number?

To continue a simple experiment, take the Student class above, where we didn’t explicitly declare a serialVersionUID field.

We first call the serialize() method above to serialize a Student object to the student.txt file on local disk:

public static void serialize(a) throws IOException {

    Student student = new Student();
    student.setName("CodeSheep");
    student.setAge( 18 );
    student.setScore( 100 );

    ObjectOutputStream objectOutputStream = 
        new ObjectOutputStream( new FileOutputStream( new File("student.txt"))); objectOutputStream.writeObject( student ); objectOutputStream.close(); }Copy the code

Student id = studentID; Student id = studentID; Student id = studentID;

In this case, we want to retrieve the student object by deserializing it from the student.txt file:

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

Runtime discovery reported an error and threw an InvalidClassException:

The serialVersionUID number before and after serialization is not compatible!

There are at least two important things to take away from this place:

  • 1. SerialVersionUID is the unique identifier before and after serialization
  • 2. Default if not explicitly defined manuallyserialVersionUIDThe compiler will automatically declare one for it!

Question 1: SerialVersionUID Serialization ID, which can be considered as the “code” during serialization and deserialization. During deserialization, the JVM will compare the serial number ID in the byte stream with the serial number ID in the serialized class.

Second question: If a serializable class is not explicitly assigned a serialVersionUID when it is defined, the Java runtime environment automatically generates a default serialVersionUID for it based on all aspects of the class. Once the structure or information of the class is changed as above, The serialVersionUID of the class changes as well!

Therefore, it is recommended that all implements Serializable classes explicitly declare a serialVersionUID value for them.

Of course, if you don’t want to assign manually, you can also use the IDE’s auto-add feature, such as IntelliJ IDEA I used. Pressing Alt + Enter automatically generates and adds the serialVersionUID field for the class, which is very convenient:


Two special cases

  • 1, all bystaticModified fields are not serialized
  • 2, All bytransientFields modified by modifiers are also not serialized

For the first point, since serialization stores the state of the object, not the state of the class, it is reasonable to omit the static static field.

For the second point, you need to understand the role of transient modifiers.

If you do not want a field to be serialized when serializing an object of a class (for example, the field holds a private value, such as password), then you can use the TRANSIENT modifier to modify the field.

For example, add a password field to the Student class defined earlier, but do not want to serialize to TXT.

When serializing the Student object, the password field is set to the default value null, as can be seen from the result of deserialization:


Serialization is controlled and enhanced

Binding blessing

As can be seen from the above process, the process of serialization and deserialization is actually a vulnerability, because there is an intermediate process from serialization to deserialization. If someone gets the intermediate byte stream, and then falsified or tampered with it, the deserialized object will have a certain risk.

After all, deserialization is also equivalent to an “implicit” object construct, so we want to do a controlled object deserialization at deserialization time.

How is that controlled?

The answer is: Write your own readObject() function for deserialization constructs of objects, thus providing constraints.

Now that you write your own readObject() function, you can do a lot of things you can control: all sorts of judgment work, for example.

The readObject() function is used to control the deserialization of the Student class. The readObject() function is used to control the deserialization of the Student class.

private void readObject( ObjectInputStream objectInputStream ) throws IOException, ClassNotFoundException {

    // Call the default deserialization function
    objectInputStream.defaultReadObject();

    // Manually check the validity of the result after deserialization, if there is a problem, terminate the operation!
    if( 0 > score || 100 < score ) {
        throw new IllegalArgumentException("Students can only score between 0 and 100!"); }}Copy the code

For example, if I intentionally change the student’s score to 101, the deserialization stops immediately with an error:

ObjectStreamClass {readObject();}} ObjectStreamClass {readObject();}}

It’s the reflex mechanism again! Yes, in Java, everything can be “reflected” (funny), even private methods defined in a class can be pulled out and executed, which makes it very comfortable.

Singleton pattern enhancement

It’s easy to overlook the fact that serializable singleton classes may not be instances alone!

Let’s take a little code example.

For example, here we write a common “static inner class” singleton pattern implementation in Java:

public class Singleton implements Serializable {

    private static final long serialVersionUID = -1576643344804979563L;

    private Singleton(a) {}private static class SingletonHolder {
        private static final Singleton singleton = new Singleton();
    }

    public static synchronized Singleton getSingleton(a) {
        returnSingletonHolder.singleton; }}Copy the code

Then write a validation main function:

public class Test2 {

    public static void main(String[] args) throws IOException, ClassNotFoundException {

        ObjectOutputStream objectOutputStream =
                new ObjectOutputStream(
                    new FileOutputStream( new File("singleton.txt")));// Serialize the singleton to the text file singleton.txt first
        objectOutputStream.writeObject( Singleton.getSingleton() );
        objectOutputStream.close();

        ObjectInputStream objectInputStream =
                new ObjectInputStream(
                    new FileInputStream( new File("singleton.txt")));// Deserialize the object in the text file singleton.txt to singleton1
        Singleton singleton1 = (Singleton) objectInputStream.readObject();
        objectInputStream.close();

        Singleton singleton2 = Singleton.getSingleton();

        // False!System.out.println( singleton1 == singleton2 ); }}Copy the code

After running, we found that the deserialized singleton was not equal to the original singleton, which undoubtedly failed to achieve our goal.

The solution is to circumvent this by writing readResolve() in the singleton class and returning the singleton directly:

private Object readResolve(a) {
    return SingletonHolder.singleton;
}
Copy the code

This way, readResolve() is called when deserialization reads objects from the stream, replacing the newly deserialized object with the returned object.


I didn’t think

I thought this article would be finished quickly, but I pulled out so many things, but I still felt a lot clearer after a combing and a series.

I’ll see you in the next post.

Github open Source project: github.com/hansonwang9… Has been included, there are detailed self-study programming learning routes, interview questions and interviews, programming information and a series of technical articles, resources continue to update…


Slower, faster