Interviewer: "talk about Java serialization"

preface

Java serialization is certainly no stranger to you. When using some open source frameworks, such as dubbo, You must have stepped on the case where the entity class does not implement the serialization interface (java.io.Serializable) and reported an error. Have you ever thought about why you should serialize the entity class? What should you do if the entity class references a class that cannot be serialized? Let's talk about the Java serialization I explored and its use scenarios.

How to serialize

  1. First, the entity class should implement the Serializable interface
public class Student implements java.io.Serializable {
    private String name;
    private int age;
    // getter setter
    ...
}
  1. You can then serialize to a local file using ObjectOutStream
// Create output stream
ObjectOutStream out = new ObjectOutputStream(new FileOutputStream("student.dat"))
// Create an object that needs to be serialized    
Student jack = new Student("Jack", 21);
Student jim = new Student("Jim", 20);
// Write stream
out.writeObject(jack);
out.writeObject(jim);

How to deserialize

// Read back object data
ObjectInputStream in = new ObjectInputStream(new FileInputStream("student.dat"));
// Then use the readObject method to get them in the order in which they are written
Student jack = (Student) in.readObject();
Student jim = (Student) in.readObject();

Method call order

Note: the serialization write order and deserialization read order should be consistent

For example:

writeInt(a), writeInt(b)

readInt(a), readInt(b)

What should I do to reference properties that cannot be serialized?

It's simple. There are several ways

  1. The java keyword {transient is used above the attribute
  2. The method is annotated with @ Transient
  3. If conditions permit, it can also be changed to static attribute

Because the static data is not in heap memory, but in the static method area

Complete example

Let's take a look at how the source code of our commonly used java ArrayList completes serialization

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
    private static final long serialVersionUID = 8683452581122892189L;
    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * The size of the ArrayList (the number of elements it contains).
     *
     * @serial
     */
    private int size;
    
    ···
    
    /**
     * Save the state of the <tt>ArrayList</tt> instance to a stream (that
     * is, serialize it).
     *
     * @serialData The length of the array backing the <tt>ArrayList</tt>
     *             instance is emitted (int), followed by all of its elements
     *             (each an <tt>Object</tt>) in the proper order.
     */
    private void writeObject(java.io.ObjectOutputStream s)
        throws java.io.IOException{
        // Write out element count, and any hidden stuff
        int expectedModCount = modCount;
        s.defaultWriteObject();

        // Write out size as capacity for behavioural compatibility with clone()
        s.writeInt(size);

        // Write out all elements in the proper order.
        for (int i=0; i<size; i++) {
            s.writeObject(elementData[i]);
        }

        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
    }

    /**
     * Reconstitute the <tt>ArrayList</tt> instance from a stream (that is,
     * deserialize it).
     */
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        elementData = EMPTY_ELEMENTDATA;

        // Read in size, and any hidden stuff
        s.defaultReadObject();

        // Read in capacity
        s.readInt(); // ignored

        if (size > 0) {
            // be like clone(), allocate array based upon size not capacity
            ensureCapacityInternal(size);

            Object[] a = elementData;
            // Read in all elements in the proper order.
            for (int i=0; i<size; i++) {
                a[i] = s.readObject();
            }
        }
    }
  1. He implemented Java io. Serializable
  2. He used the transient keyword on the field elementData array
  3. He also wrote the writeObject and readObject methods

If you use IDE to query and find that these two methods do not implement any interface, when will they be called? After consulting the data, we found that Java io. Serializable notes say

Classes that require special handling during the serialization and
deserialization process must implement special methods with these exact
signatures:
<PRE>
private void writeObject(java.io.ObjectOutputStream out)
    throws IOException
private void readObject(java.io.ObjectInputStream in)
    throws IOException, ClassNotFoundException;
private void readObjectNoData()
    throws ObjectStreamException;
</PRE>
···
@author  unascribed
@see java.io.ObjectOutputStream
@see java.io.ObjectInputStream
@see java.io.ObjectOutput
@see java.io.ObjectInput
@see java.io.Externalizable
@since   JDK1.1

In other words, these two methods are special callback methods. When your entity class is very special and needs to be serialized manually, you can implement these two methods manually

Then you can return to the detail. ArrayList is the writeObject that loops the element data array

Manually serialize and deserialize objects

Hook methods supported by Serializable interface

Let's first look at the definition of jdk serialization interface

Classes that require special handling during the serialization and
deserialization process must implement special methods with these exact
signatures:
<PRE>
private void writeObject(java.io.ObjectOutputStream out)
    throws IOException
private void readObject(java.io.ObjectInputStream in)
    throws IOException, ClassNotFoundException;
private void readObjectNoData()
    throws ObjectStreamException;
</PRE>
···
@author  unascribed
@see java.io.ObjectOutputStream
@see java.io.ObjectInputStream
@see java.io.ObjectOutput
@see java.io.ObjectInput
@see java.io.Externalizable
@since   JDK1.1

It tells us that we can write writeObject and readObject to serialize manually (not affected by keyword modification)

Serialized usage scenarios

  1. For example, the network transmission of common Java objects
  2. java small game development is more convenient to store complex relationships than xml when storing game data

It's a storage method. You can use it as long as you need it

summary

  1. Serialization uses Java io. Serializable interface
  2. Static fields are not serialized
  3. transient keyword for field masking
  4. Customize serialization to write readObject and writeObject methods
  5. The write order and read order should be consistent. Even if they are not used, they need to read

Keywords: Java

Added by justAnoob on Sun, 02 Jan 2022 20:30:21 +0200