[Original] (translation) Java serialization magic methods and use examples

Please indicate the source.

English Original Address: www.javacodegeeks.com/2019/09/jav...

Translation: Formalin / Vipassana

In the previous article Everything You Need to Know About Java Serialization we discussed how to implement Serializableto activate the ability to serialize a class interface. If we had not realized Serializablethe interface, or it refers to a non-serializable class, JVM will throw NotSerializableExceptionnon-sequence exception.

SerializableAll subclasses are also serializable, which also includes Externalizablean interface. So even though we have customized serialization process by Externalizable , our class also still be serialized.

SerializableIt is a marker interface. It is not a field, there is no way. Its role is only to provide a marker for the JVM. The real process sequence is controlled by the JVM ObjectInputStreamand ObjectOutputStreamclass.

If we want to add additional processing logic on top of the normal processing flow, how to do it? For example, we want to add / decryption of sensitive data before serialization / de-serialization. Java provides some additional methods to help us achieve this aim, which is the subject of our discussion is about in this blog.

writeObject and readObject methods

Want to customize or add additional logic to enhance the serialization deserialization process / need to provide writeObjectand readObjecttwo method signature as follows:

  • private void writeObject(java.io.ObjectOutputStream out) throws IOException
  • private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException

These methods Everything You Need to Know About Java Serialization has been discussed in detail before.

readObjectNoData 方法

As Serializablein the docs java, if we want when received serialized stream is not satisfied we want deserialization class, automatically carry out some initialization state, then we must provide readObjectNoDataa method signature as follows:

  • private void readObjectNoData() throws ObjectStreamException

This may occur in the receiver uses a different version of a class with the sender. Extended versions of the recipient of more than a few fields, but there is no version of the sender of these fields. Another possibility is that the serialization stream has been tampered with. At this time, whether it is malicious or not full stream flow, can be used readObjectNoDatato initialize the sequence of the resulting object to the correct state approach.

Each class serializable can define its own readObjectNoDatamethod. If a class does not define readObjectNoDataa method, then when the above situation, the value of these fields will default value. (Annotation: such as null or 0)

writeReplace and methods readResolve

Serializable class, if you want when an object is written to the stream, a certain degree of conversion, this particular method may be provided:

  • ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException

If you want to read when the object from the stream, a certain degree of replacement, you can provide the following methods:

  • ANY-ACCESS-MODIFIER Object readResolve() throws ObjectStreamException

Basically, the writePlacemethod allows the developer to provide an alternative object to replace the original object to be serialized. The readResolvemethod is used with the object of your choice instead of the original deserialize objects out deserialization time.

and a main purpose writeReplace readResolve method is used to achieve a singleton. We know deserialized every time a new object is created , often used to make a deep copy of the object . In the case of a single embodiment should not Haonong mode.

More information can be found Java cloning and serialization on Java Cloning and Java Serialization topic.

readResolveMethod will readObjectcall after the method returns (In contrast, the writeReplacemethod is writeObjectbeing called before the method). readResolveMethod returns the object will be replaced ObjectInputStream.readObjectback to the user thisobject and update all references to reverse the stream object. We can use the writeReplacemethod to the object to be serialized into null, then readResolvea single exemplary embodiment of the object to deserialize the result of alternative methods. (Annotation: This would solve the problem of a single case on two proposed)

validateObject method

If we want to do check some of our fields, we can implement ObjectInputValidationthe interface validateObjectmethods.

validateObjectThe method we will readObjectcall the method in ObjectInputStream.registerValidation(this, 0)is called when the registration check. It has not been tampered with or the actual flow for effective verification useful.

Finally, the sample code for all of these above methods.

public class SerializationMethodsExample {
 
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Employee emp = new Employee("Naresh Joshi", 25);
        System.out.println("Object before serialization: " + emp.toString());
 
        // Serialization
        serialize(emp);
 
        // Deserialization
        Employee deserialisedEmp = deserialize();
        System.out.println("Object after deserialization: " + deserialisedEmp.toString());
 
 
        System.out.println();
 
        // This will print false because both object are separate
        System.out.println(emp == deserialisedEmp);
 
        System.out.println();
 
        // This will print false because both `deserialisedEmp` and `emp` are pointing to same object,
        // Because we replaced de-serializing object in readResolve method by current instance
        System.out.println(Objects.equals(emp, deserialisedEmp));
    }
 
    // Serialization code
    static void serialize(Employee empObj) throws IOException {
        try (FileOutputStream fos = new FileOutputStream("data.obj");
             ObjectOutputStream oos = new ObjectOutputStream(fos))
        {
            oos.writeObject(empObj);
        }
    }
 
    // Deserialization code
    static Employee deserialize() throws IOException, ClassNotFoundException {
        try (FileInputStream fis = new FileInputStream("data.obj");
             ObjectInputStream ois = new ObjectInputStream(fis))
        {
            return (Employee) ois.readObject();
        }
    }
}
 
class Employee implements Serializable, ObjectInputValidation {
    private static final long serialVersionUID = 2L;
 
    private String name;
    private int age;
 
    public Employee(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    // With ObjectInputValidation interface we get a validateObject method where we can do our validations.
    @Override
    public void validateObject() {
        System.out.println("Validating age.");
 
        if (age < 18 || age > 70)
        {
            throw new IllegalArgumentException("Not a valid age to create an employee");
        }
    }
 
    // Custom serialization logic,
    // This will allow us to have additional serialization logic on top of the default one e.g. encrypting object before serialization.
    private void writeObject(ObjectOutputStream oos) throws IOException {
        System.out.println("Custom serialization logic invoked.");
        oos.defaultWriteObject(); // Calling the default serialization logic
    }
 
    // Replacing de-serializing object with this,
    private Object writeReplace() throws ObjectStreamException {
        System.out.println("Replacing serialising object by this.");
        return this;
    }
 
    // Custom deserialization logic
    // This will allow us to have additional deserialization logic on top of the default one e.g. performing validations, decrypting object after deserialization.
    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        System.out.println("Custom deserialization logic invoked.");
 
        ois.registerValidation(this, 0); // Registering validations, So our validateObject method can be called.
 
        ois.defaultReadObject(); // Calling the default deserialization logic.
    }
 
    // Replacing de-serializing object with this,
    // It will will not give us a full proof singleton but it will stop new object creation by deserialization.
    private Object readResolve() throws ObjectStreamException {
        System.out.println("Replacing de-serializing object by this.");
        return this;
    }
 
    @Override
    public String toString() {
        return String.format("Employee {name='%s', age='%s'}", name, age);
    }
}
复制代码

You can also at Github repository find the complete code, welcome any feedback.

Guess you like

Origin juejin.im/post/5d7206c5f265da03ab427181