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