preface

Java object “implements Serializable” So, what does it do? This paper analyzes the sequence from the following points of view

  • What is Java serialization?
  • Why serialization?
  • Serialization purpose
  • Common APIS for Java serialization
  • Use of serialization
  • Serialization layer
  • Daily development of serialization considerations
  • Serialization is common

What is Java serialization?

  • Serialization: The process of converting Java objects into byte sequences
  • Antisequence: The process of restoring a sequence of bytes to Java objects

Why serialization?

Java objects run in the JVM’s heap memory, and their lives come to a screeching halt when the JVM stops.

Metaphorically, as a wandering yard farmer in a big city, moving is the norm. When we move a desk, it’s too big to fit through a smaller door, so we have to take it apart and move it through, and the process of taking it apart is called serialization. The process of putting the desk back together is deserialization.

Serialization purpose

Serialization allows objects to exist independently of the program and serves two main purposes:

  • 1) Serialization mechanism allows objects to be stored on hard disk, reducing memory pressure and playing a role of persistence;

For example, the Session object in the Web server, when there are more than 100,000 concurrent users access, there may be 100,000 Session objects, memory may indigestion, so the Web container will first serialize some Seesion to the hard disk, and then restore the objects saved in the hard disk to the memory when they need to use.

  • 2) Serialization mechanism makes it possible for Java objects to be transported over the network.

When we use Dubbo to call the service framework remotely, we need to implement the Serializable interface for the transferred Java objects, so that the objects can be transferred over the network.

Java serialization API

java.io.ObjectOutputStream
java.io.ObjectInputStream
java.io.Serializable
java.io.Externalizable
Copy the code

The Serializable interface

The Serializable interface is a tag interface with no methods or fields. Once this interface is implemented, objects of that class are signaled to be serializable.

public interface Serializable {
}
Copy the code

Externalizable interface

Externalizable inherits the Serializable interface and defines two abstract methods: WriteExternal () and readExternal(). If a developer uses Externalizable for serialization and deserialization, the writeExternal() and readExternal() methods need to be overridden

public interface Externalizable extends java.io.Serializable {
    void writeExternal(ObjectOutput out) throws IOException;
    void readExternal(ObjectInput in) throws IOException, ClassNotFoundException;
}
Copy the code

Java. IO. ObjectOutputStream classes

Represents an Object output stream, whose writeObject(Object obj) method serializes the specified OBj Object parameters and writes the resulting byte sequence to a target output stream.

java.io.ObjectInputStream

Represents an object input stream, whose readObject() method reads a sequence of bytes from the input stream, deserializes it into an object, and finally returns it.

Use of serialization

How is serialization used? Let’s take a look at some key aspects of serialization:

  • Declare an entity class that implements the Serializable interface
  • The writeObject method of ObjectOutputStream is used to achieve serialization
  • Deserialization is implemented using ObjectInputStream’s readObject method

Declare a Student class that implements Serializable

public class Student implements Serializable {

    private Integer age;
    private String name;

    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) { this.name = name; }}Copy the code

Serialize the Student object using the writeObject method of the ObjectOutputStream class

Serialize the Student object to a file

ObjectOutputStream objectOutputStream = new ObjectOutputStream( new FileOutputStream("D:\\text.out"));
Student student = new Student();
student.setAge(25);
student.setName("jayWei");
objectOutputStream.writeObject(student);

objectOutputStream.flush();
objectOutputStream.close();
Copy the code

The test.out file looks like this:

Use ObjectInputStream’s readObject method to deserialize and regenerate the Student object

Read the test.out file and deserialize it into the Student object

ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\text.out"));
Student student = (Student) objectInputStream.readObject();
System.out.println("name="+student.getName());
Copy the code

Serialization layer

The Serializable underlying

Serializable interface is an empty interface with no methods or fields.

public interface Serializable {
}
Copy the code

To verify the Serializable interface, remove the Student object from the above demo and see how Serializable can be serialized

An exception was raised during serialization.

Exception in thread "main" java.io.NotSerializableException: com.example.demo.Student
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
	at com.example.demo.Test.main(Test.java:13)
Copy the code

Take a look at the stack of information, the original significant discovery, as follows ~

The bottom layer looks like this:
Serializable is really just a flag, a serialization flag

WriteObject (Object)

The serialization method is writeObject. Based on the above demo, we will analyze the core method call chain of writeObject.

WriteObject directly calls the writeObject0 () method,

public final void writeObject(Object obj) throws IOException {
    ......
    writeObject0(obj, false); . }Copy the code

The main implementation of writeObject0 is different types of objects, which call different methods to write serialized data. In this case, if the object implements the Serializable interface, the writeOrdinaryObject() method ~ is called

private void writeObject0(Object obj, boolean unshared) throws IOException { ...... / / type Stringif(obj instanceof String) { writeString((String) obj, unshared); // Array type}else if(cl.isArray()) { writeArray(obj, desc, unshared); // Enumeration type}else if(obj instanceof Enum) { writeEnum((Enum<? >) obj, desc, unshared); //Serializable implements serialization interface}else if (obj instanceof Serializable) {
        writeOrdinaryObject(obj, desc, unshared);
    } else{// Other cases throw exceptions ~if (extendedDebugInfo) {
            throw new NotSerializableException(
                cl.getName() + "\n" + debugInfoStack.toString());
        } else{ throw new NotSerializableException(cl.getName()); }}...Copy the code

WriteOrdinaryObject () calls writeClassDesc(desc) to write the class’s generated information, and then calls the writeSerialData method to write serialized data

private void writeOrdinaryObject(Object obj, ObjectStreamClass desc, boolean unshared) throws IOException { ...... // Call ObjectStreamClass writeClassDesc(desc,false); // Check whether the Externalizable interface is implementedif(desc.isExternalizable() && ! desc.isProxy()) { writeExternalData((Externalizable) obj); }else{// Write serialized data writeSerialData(obj, desc); }... }Copy the code

WriteSerialData () implements writing field data to the serialized object

  private void writeSerialData(Object obj, ObjectStreamClass desc)
        throws IOException
    {
        for (int i = 0; i < slots.length; i++) {
            if(slotDesc hasWriteObjectMethod ()) {/ / if the serialized objects custom implements writeObject () method, execute the code block slotDesc. InvokeWriteObject (obj, this); }else{// Call the default method to write instance data defaultWriteFields(obj, slotDesc); }}}Copy the code

DefaultWriteFields () gets the basic data type of the class and writes it directly to the underlying byte container; Get the obJ data of the class, loop recursively call writeObject0(), write data ~

Private void defaultWriteFields(Object obj, ObjectStreamClass desc) throws IOException {// Get the basic data type of the class. Save to primVals byte array desc.getPrimfieldValues (obj, primfieldValues); Bout. Write (primVals, 0, primDataSize,false); ObjectStreamField[] fields = desc. GetFields (ObjectStreamField[] fields = desc.false); Object[] objVals = new Object[desc.getNumObjFields()]; int numPrimFields = fields.length - objVals.length; GetObjFieldValues (obj, objJ); // Get the values of type OBj and save them to objVals byte array desc.getObjFieldValues(obj, objJ); // For all fields of type Object, loopfor(int i = 0; i < objVals.length; i++) { ...... WriteObject0 (objprimfields [I], fields[numPrimFields + I].isunshared ()); . }}Copy the code

7. Some considerations for daily development of serialization

  • Static static variables and transient modified fields are not serialized
  • SerialVersionUID problem
  • If the member variable of a serialized class is an object type, the class of that object type must implement serialization
  • The subclass realizes serialization, the parent class does not realize serialization, and the field loss problem in the parent class

Static static variables and transient modified fields are not serialized

Class gender (gender); class specialty (gender)

public class Student implements Serializable {

    private Integer age;
    private String name;

    public static String gender = "Male";
    transient  String specialty = "Computer science";

    public String getSpecialty() {
        return specialty;
    }

    public void setSpecialty(String specialty) {
        this.specialty = specialty;
    }

    @Override
    public String toString() {
        return "Student{" +"age=" + age + ", name='" + name + '\'' + ", gender='" + gender + '\'' + ", specialty='" + specialty + '\' ' +
                '} '; }...Copy the code

Print the student object, serialize it to a file, then modify the value of the static variable, deserialize again, and output the deserialized object ~

Student{age=25, name='jayWei', gender='male', specialty='Computer Science'} Student{age=25, name='jayWei', gender='woman', specialty='null'}
Copy the code

The comparison results can be found:

  • 1) The gender of the static variable before serialization was clearly “male”, and then changed in the program after serialization, but changed to “female” after deserialization, what? It is obvious that this static property has not been serialized. Static member variables are class-level variables, and serialization is for objects.
  • 2) After the serialization and deserialization process, the value of the specialty field variable from ‘computer major’ becomes null. The transient keyword prevents the transient field from being serialized to the file. After deserialization, the transient field value is set to the initial value, for example, the int value is set to 0, and the object value is set to NULL.

SerialVersionUID problem

SerialVersionUID Every class that implements the Serializable interface has a static variable representing the Serializable version identifier, either equal to 1L by default or equal to the object’s hash code.

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

What is the use of serialVersionUID?

The mechanism for JAVA serialization is to verify version consistency by determining the serialVersionUID of a class. When deserializing, the JVM compares the serialVersionUID in the incoming byte stream with the serialVersionUID of the corresponding local entity class. If they are the same, the deserialization succeeds. If they are different, an InvalidClassException is thrown.

Next, let’s verify by modifying the Student class and deserializing the operation

Exception in thread "main" java.io.InvalidClassException: com.example.demo.Student;
local class incompatible: stream classdesc serialVersionUID = 3096644667492403394,
local class serialVersionUID = 4429793331949928814
	at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:687)
	at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1876)
	at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1745)
	at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:2033)
	at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1567)
	at java.io.ObjectInputStream.readObject(ObjectInputStream.java:427)
	at com.example.demo.Test.main(Test.java:20)
Copy the code

As you can see from the log stack exception information, the class in the file stream is different from the class in the current classpath. Their serialVersionUID is different, so deserialization throws an InvalidClassException. So what if you do need to modify the Student class, and you want to deserialize successfully? You can specify the value of serialVersionUID manually, typically set to 1L or, alternatively, let our editor IDE generate it

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

In fact, Ali’s development manual mandates that the serialVersionUID field ~ cannot be modified when new attributes are added to serialized classes

If the member variable of a serialized class is an object type, the class of that object type must implement serialization

Add a Teacher member variable to the Student class, where Teacher does not implement the serialization interface

public class Student implements Serializable { private Integer age; private String name; private Teacher teacher; . } //Teacher does not implement public class Teacher {...... }Copy the code

Serializableexception NotSerializableException

Exception in thread "main" java.io.NotSerializableException: com.example.demo.Teacher
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
	at java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1548)
	at java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1509)
	at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1432)
	at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1178)
	at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
	at com.example.demo.Test.main(Test.java:16)
Copy the code

This can be found in the underlying source code analysis in the previous section. An Object serialization process will loop through its Object field and recursively call the serialized class. That is, when serializing Student, it will serialize Teacher, but there is no serialization interface implemented for Teacher. Therefore, NotSerializableException is thrown. So if the member variable of an instantiated class is an object type, the class of that object type must implement serialization

If a subclass implements Serializable, its parent class does not implement the Serializable interface.

The subclass Student implements the Serializable interface, while the parent class User does not

Public class Student extends User implements Serializable {private Integer age; private String name; } // Serializable interface public class User {String userId; } Student student = new Student(); student.setAge(25); student.setName("jayWei");
student.setUserId("1");

ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:\\text.out")); objectOutputStream.writeObject(student); objectOutputStream.flush(); objectOutputStream.close(); ObjectInputStream ObjectInputStream = new ObjectInputStream(new FileInputStream("D:\\text.out"));
Student student1 = (Student) objectInputStream.readObject();
System.out.println(student1.getUserId());
//output
/** 
 * null
 */
Copy the code

From the deserialization results, you can see that the parent class attribute value is missing. Therefore, a subclass implements the Serializable interface. If the parent class does not implement the Serializable interface, the parent class is not serialized.

Serialization often meet test questions

  • How does serialization work at the bottom?
  • When serializing, how do I keep some members from being serialized?
  • What is the difference between Serializable and Externalizable in Java
  • What is the use of serialVersionUID?
  • Can you customize the serialization process, or can you override the default serialization process in Java?
  • What variables are not serialized during Java serialization?

1. How is serialization implemented at the bottom?

The Serializable keyword can also be used to write basic types directly, obtain obJ data, and loop recursively to writeObject

2. When serializing, how to make certain members not serialized?

The transient keyword can be used to prevent the modified field from being serialized to the file. After deserialization, the transient field value is set to the initial value, for example, the int value will be set to 0, and the object value will be set to NULL.

3. What is the difference between Serializable and Externalizable in Java

Externalizable inherits Serializable, giving us the writeExternal() and readExternal() methods that allow us to control Java’s serialization mechanism without relying on Java’s default serialization. Properly implementing the Externalizable interface can significantly improve application performance.

4. What does serialVersionUID do?

As you can see in section 7 of this article, the mechanism for JAVA serialization is to verify version consistency by determining the serialVersionUID of a class. When deserializing, the JVM compares the serialVersionUID in the incoming byte stream with the serialVersionUID of the corresponding local entity class. If they are the same, the deserialization succeeds. If they are different, an InvalidClassException is thrown.

5. Can you customize the serialization process, or can you override the default serialization process in Java?

You can. As we all know, to serialize an object to call the ObjectOutputStream. WriteObject (saveThisObject), and the ObjectInputStream. ReadObject () reads the object, But one more thing that the Java VIRTUAL machine gives you is to define these two methods. If these two methods are defined in a class, the JVM calls them instead of applying the default serialization mechanism. At the same time, you can declare these methods private to avoid being inherited, overridden, or overridden.

6. Which variables were not serialized during Java serialization?

Static static variables and transient modified fields are not serialized. Static member variables are class-level, whereas serialization is for objects. The TRANSIENT keyword modifies the field ornament to prevent the field from being serialized to the file.

Reference and thanks

  • Java Basics – Serialization and deserialization of Java objects
  • 10 Tough Java Interview Questions and Answers

Personal public account

  • If you think it’s good, give me a thumbs up and attention. Thank you
  • If there is any incorrect place, please kindly point out, thank you very much.
  • At the same time, I am looking forward to friends can pay attention to my public account, behind slowly launch better dry goods ~ hee hee
    • Github address: github.com/whx123/Java…