A problem can appear when the class definition available on JVM#1 is not the same as the one available on JVM#2.
When defining a class implementing the java.io.Serializable interface, modern compilers will often reproach you with not correctly defining a final static long serialVersionUID.
We are briefly going to touch base on that, trying to explain what that means and what this is all about.
There is nothing like a good example to illustrate this kind of things. I hope you will like the following one.
Let us define a Serializable class:
public class Description implements Serializable { @SuppressWarnings("compatibility:5602934367223767090") private static final long serialVersionUID = 1L; private String stringDataOne; private String stringDataTwo; //private double numericData; private float numericData; public void setStringDataOne(String s) { this.stringDataOne = s; } public void setStringDataTwo(String s) { this.stringDataTwo = s; } public String getStringDataOne() { return this.stringDataOne; } public String getStringDataTwo() { return this.stringDataTwo; } public void setNumericData(float dummyNumber) //public void setNumericData(double dummyNumber) { this.numericData = dummyNumber; } public float getNumericData() //public double getNumericData() { return numericData; } @Override public String toString() { return new StringBuffer("One : ").append(this.stringDataOne) .append("\nTwo : ").append(this.stringDataTwo) .append("\nNum : " + NumberFormat.getInstance().format(numericData)).toString(); } }Notice the final static long serialVersionUID = 1;. We'll talk about it shortly.
This class being defined, we are going to:
- Serialize an instance of it into a file
- De-serialize this instance from the file
Serializer
public class WriteObject { public static void main(String[] args) { Description dataObject = new Description(); dataObject.setStringDataOne("First line of data"); dataObject.setStringDataTwo("Second line, as expected"); dataObject.setNumericData(12345); try { FileOutputStream fout = new FileOutputStream("description.ser"); ObjectOutputStream oos = new ObjectOutputStream(fout); oos.writeObject(dataObject); oos.close(); System.out.println("Done"); } catch (Exception ex) { ex.printStackTrace(); } } }Serializer at work:
Done
De-serializer
public class ReadObject { public static void main(String[] args) { Description restoredObject; try { FileInputStream fin = new FileInputStream("description.ser"); ObjectInputStream ois = new ObjectInputStream(fin); restoredObject = (Description) ois.readObject(); ois.close(); System.out.println(restoredObject); } catch (Exception ex) { ex.printStackTrace(); } } }De-serializer at work:
One : First line of data Two : Second line, as expected Num : 12,345This is what happens when everything goes well.
Now, let's modify the Serializable class, let's change the float numeric data to a double (member, getter, setter), and without re-serializing it, let's try to de-serialize it:
java.io.InvalidClassException: serialversionui.Description; incompatible types for field numericData at java.io.ObjectStreamClass.matchFields(ObjectStreamClass.java:2210) at java.io.ObjectStreamClass.getReflector(ObjectStreamClass.java:2105) at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:602) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1582) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350) at serialversionui.ReadObject.main(ReadObject.java:17)Basically, it says it cannot cast a Description to a Description...
If the serialVersionUID had been modified when changing the float to a double - like from 1 to 2 for example
private static final long serialVersionUID = 2L;you would have seen the following message:
java.io.InvalidClassException: serialversionui.Description; local class incompatible: stream classdesc serialVersionUID = 1, local class serialVersionUID = 2 at java.io.ObjectStreamClass.initNonProxy(ObjectStreamClass.java:562) at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1582) at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1495) at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1731) at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1328) at java.io.ObjectInputStream.readObject(ObjectInputStream.java:350) at serialversionui.ReadObject.main(ReadObject.java:17)It does not work any better, but it returns useful information to communicate with the development teams.
Java recommends a way to come up with the appropriate value for the serialVersionUID. This is the reason for the @SuppressWarnings annotation in the code snippets available above. Find it here.
No comments:
Post a Comment