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 Serializable
to activate the ability to serialize a class interface. If we had not realized Serializable
the interface, or it refers to a non-serializable class, JVM will throw NotSerializableException
non-sequence exception.
Serializable
All subclasses are also serializable, which also includes Externalizable
an interface. So even though we have customized serialization process by Externalizable , our class also still be serialized.
Serializable
It 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 ObjectInputStream
and ObjectOutputStream
class.
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 writeObject
and readObject
two 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 Serializable
in 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 readObjectNoData
a 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 readObjectNoData
to initialize the sequence of the resulting object to the correct state approach.
Each class serializable can define its own readObjectNoData
method. If a class does not define readObjectNoData
a 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 writePlace
method allows the developer to provide an alternative object to replace the original object to be serialized. The readResolve
method 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.
readResolve
Method will readObject
call after the method returns (In contrast, the writeReplace
method is writeObject
being called before the method). readResolve
Method returns the object will be replaced ObjectInputStream.readObject
back to the user this
object and update all references to reverse the stream object. We can use the writeReplace
method to the object to be serialized into null, then readResolve
a 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 ObjectInputValidation
the interface validateObject
methods.
validateObject
The method we will readObject
call 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.