What are the pros and cons of using serialVersionUID and @SuppressWarnings("serial") on classes implementing Serializable?
This question has been the subject of some lively discussions in my team. My personal choice is to use
@SuppressWarnings("serial")
My thoughts are that it means there is one less thing to maintain compared to using
serialVersionUID
Am I correct in thinking that using this allows the compiler to generate the UID and is therefore more likely to pick up changes to the class?
My biggest fear is that relying on a developer to change the UID 开发者_如何学Pythonwhen they change the class is more likely to lead to unforeseen errors.
Are there any pitfalls to my approach? Has anyone else had good or bad experiences using either of these approaches?
It boils down to this questions:
- Shall the serialized streams be read and written by the just same code or by different code?
"Different code" can mean several things:
- old versions vs. new versions
- two independent programs with perhaps old and new libraries
- more of stuff like that.
In these cases you should strongly adhere to the Serialization contracts - and this not done by just setting serialVersionUId
- usually you must also overwrite the methods for serialization and deserialization to cope with different versions.
If - on the other hand - the same program reads and writes the stuff for something like an internal cache and that cache can be rebuild from scratch on when the software is updated - then feel free to make your life as easy as you can.
Between these extremes are of course various shades of grey.
As usual, the definitive answer is in its own chapter of Effective Java.
TL;DR. Using @SuppressWarnings("serial")
is fine (and good for removing the warning) unless you actually want to serialize the class. In that case, you want to actually implement serialVersionUID
, and do it properly, regenerating (or at least changing) it after every change in the class that would cause problems in deserialization.
When serialising an Object, if it has a serial identifier it will be used, otherwise it will be generated.
If the serial identifier is missing and the javac
option -Xlint
is specified, a warning will be generated. This warning can be muted with @SuppressWarnings("serial")
. The purpose of this annotation is just to suppress the warning, it will not interfere in any other way with the serialisation.
From Serializable Javadoc:
The serialization runtime associates with each serializable class a version number, called a serialVersionUID, which is used during deserialization to verify that the sender and receiver of a serialized object have loaded classes for that object that are compatible with respect to serialization.
and also:
If a serializable class does not explicitly declare a serialVersionUID, then the serialization runtime will calculate a default serialVersionUID value for that class based on various aspects of the class, as described in the Java(TM) Object Serialization Specification. However, it is strongly recommended that all serializable classes explicitly declare serialVersionUID values, since the default serialVersionUID computation is highly sensitive to class details that may vary depending on compiler implementations, and can thus result in unexpected InvalidClassExceptions during deserialization.
Example: serialising a class with @SuppressWarnings("serial")
Test.java : the class to be serialised
import java.io.Serializable;
@SuppressWarnings("serial")
class Test implements Serializable {
int a = 0;
public Test() {
}
public Test(int arg) {
a = arg;
}
}
Writer.java : the class that writes an instance of Test to the file test.serial
import java.io.*;
class Writer {
public static void main(String[] args) {
Test t = new Test(2);
String filename = "test.serial";
try {
FileOutputStream fos = new FileOutputStream(filename);
ObjectOutputStream out = new ObjectOutputStream(fos);
out.writeObject(t);
out.close();
} catch (IOException ex) {
ex.printStackTrace();
}
}
}
Not using serialVersionUID
is the conservative approach. Any change in the serialized format will be considered an incompatible change. It means you may find errors when deserializing objects written by an older version, that might actually make sense to deserialize, had you written custom deserialization code and used serialVersionUID
.
Using it has the benefit of possibly preserving compatibility across more versions of the serialized object, but, introduces a new type of potential bug: if you forget to update serialVersionUID
after changing a class in an incompatible way, the best thing that can happen is that you also get an error at runtime because the data can't be read sensibly. But, it might silently succeed, perhaps ignoring or misinterpreting serialized data in the process.
So, first decide which one you want: use it or not? I definitely default to not using serialVersionUID
unless it's clearly needed, and there's a commitment to maintain it correctly.
If you don't use it, you can turn off warnings with @SuppressWarnings
, but, you can also turn it off globally in javac
with -Xlint:-serial
. That's a coarse approach -- convenient perhaps, but make sure you really want to do that. It's appropriate if, for instance, you definitely don't use serialVersionUID
anywhere.
If you think you will need to add anything to this class in the future then go ahead and hardcode serialVersionUID to 1L if you don't have serialized objects already. If you do have serialized objects already and need to stay compatible with them use the serialver tool to find out what Java has set this to and use that value.
This way you will be able to add attribute to this class in the future and be able to automatically stay compatible. If you don't do the above then this won't be handled automatically and you must write code to convert from the old classes to the new classes each time you add a field to the class.
I have to say your choice of SuppressWarning is bad. You should explicitly define serialVersionUID. Maintaining it is not that much work while it'll save your life in future.
Some time in future, you want to change the class definition (inevitable if your work is serious) while achieving backward compatibility, serialVersionUID will save your life. If you don't define it, Java will calculate serialVersionUID for you based on the class signature (?); simply reordering the variables would change the signature and break compatibility (or even adding a helper function). Seriously, defining serialVersionUID will save your life.
You should read the Serialization section in Effective Java as suggested by other folks.
精彩评论