This article is from "151 Suggestions for Improving Java"
Recommendation 11: Develop a good habit of showing the declaration UID
Let's first write a serialization and deserialization tool class SerilizationUtils
public class SerializationUtils { private static String FILE_NAME="E:/serializable.txt"; public static void writeObject(Serializable s){ try{ ObjectOutputStream oob = new ObjectOutputStream(new FileOutputStream(FILE_NAME)); oob.writeObject(s); oob.close(); }catch(Exception e){}; } public static Object readObject(){ Object obj=null; try{ ObjectInputStream oob = new ObjectInputStream(new FileInputStream(FILE_NAME)); obj = oob.readObject(); oob.close(); }catch(Exception e){}; return obj; } }
Person class
public class Person implements Serializable { /** * The declaration UID not displayed here */ //private static final long serialVersionUID = 1L; private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } // /*private String sex; public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; }*/ }
First define a message producer Produce
public class Produce { public static void main(String[] args){ Person person = new Person(); person.setName("Shi Renchuang"); //after adding sex //person.setSex("Male"); //Serialize and save to disk SerializationUtils.writeObject(person); System.out.println(person); } }
After the execution here, the person is serialized to E:/serializable.txt
then define a consumer
public class Consumer { public static void main(String[] args) throws Exception{ // deserialize Person p = (Person)SerializationUtils.readObject(); System.out.println("The value obtained by deserialization"+p.getName()); //System.out.println("The value obtained by deserialization"+p.getSex()); } }
There is no problem with running it here; but what happens if we do not refer to a Person when serializing and deserializing;
For example, before serialization, person still has only one attribute name after executing produce (serialization); add a sex attribute to person (be careful not to run produce (serialization) again);
After adding the property we run the consumer (deserialize); an error occurs
The book says InvalidClassException error; but I personally reported the above error;
Why is this so?
The reason is that the version of the class (person) corresponding to serialization and deserialization is inconsistent; the JVM cannot convert the data stream into an instance object;
How does the JVM determine the corresponding version of a class?
Is through the SerivalVersionUID, that is, the stream identifier, that is, the version definition of the class
private static final long serialVersionUID = 1L;
UIDs can be implicitly declared and explicitly declared; implicit declarations are automatically generated by the compiler Basic listings are unique; if the class is changed; it is the UID that also changes
When deserializing, jvm will compare whether the UID in the data stream is consistent with the current class (person); if it is consistent, it means that the class has not changed; if it is inconsistent, it means that it has changed, which is a good validation mechanism;
However; there are special cases; for example: my class doesn't change much, and I want it to be serialized when deserializing as well. then what should we do?
Since we are judging whether the UIDs are consistent, we can make their UIDs consistent. Displaying the declared UIDs can solve this problem very well;
Recommendation 12: Avoid using serialized classes without final assignment of invariants in constructors
private static final long serialVersionUID = 1L; //final constant is a direct quantity. It will be recalculated when deserializing to keep the new and old unification of final objects, which is conducive to the unification of code business logic public final String testFinal="Before serialization"; // public final String testFinal="After serialization";
The assignment to testFinal during serialization is before serialization
After serialization, change testFinal to After serialization
then perform deserialization
// deserialize Person p = (Person)SerializationUtils.readObject(); System.out.println("Deserialized value: "+p.testFinal);Is the output value before or after serialization?
The output result is: the value obtained by deserialization: after serialization
why? During serialization, we serialized testFinal into a data stream and stored it on disk. It stands to reason that when deserializing, we get the value before serialization. Why did it become after serialization?
This is because of one of the basic rules of serialization: keep final new and old objects the same. Conducive to the unification of code business logic; that is, if final is a direct quantity, final will be recalculated during deserialization;
Another assignment of final variables: constructor assignment
public final String testFinal; public Person() { // TODO Auto-generated constructor stub testFinal="Before constructor assignment"; }
Modification after serialization
testFinal="After constructor assignment";Then deserialize; guess: the output here should be after the constructor assignment, right? Because rule 1 is the same!
So what is the output?
Deserialized value: before constructor assignment
Why before and not after the change? The reason is another rule
Constructor is not executed when deserializing!
Recommendation 13: Avoid complex assignments to final variables
Summary: Deserialization cannot be reassigned in the following cases
1. Assign value to final variable through constructor
2. Assignment of non-final variables through methods
3. The object modified by final is not the basic object
Recommendation 14: Use private methods of serialization classes to cleverly solve some property persistence problems
......