Question re serializable

R

Rhino

I'm trying to resolve some compiler warnings that I've simply ignored for
quite a long time. Many of them are pretty obvious but I've come across a
few that aren't clear to me at all. One of these involves Serializable.

I have several occurrences of warnings of this type: "The serializable class
Foo does not declare a final serialVersionUID field of type long."

I've got dozens of messages of this kind and the classes that get the
warning message extend a variety of different classes, including JFrame,
JDialog, JApplet, but also classes that have nothing to do with GUIs, like
some homegrown Exception classes.

Can someone explain what this message is about: what it means, why I get it,
and how I can prevent it?

I've read the article about Serializable in the API but most of it goes
right over my head. I'm particularly foggy on which value I'm supposed to
use in the serialVersionUID constant if I choose to code one, which is
apparently highly-recommended.

I'm also unclear on what Bad Things are going to happen if I don't change my
code to prevent this warning.

I'd appreciate it if anyone can clarify this for me.
 
T

Thomas Hawtin

Rhino said:
I'm trying to resolve some compiler warnings that I've simply ignored for
quite a long time. Many of them are pretty obvious but I've come across a
few that aren't clear to me at all. One of these involves Serializable.

I have several occurrences of warnings of this type: "The serializable class
Foo does not declare a final serialVersionUID field of type long."

I've got dozens of messages of this kind and the classes that get the
warning message extend a variety of different classes, including JFrame,
JDialog, JApplet, but also classes that have nothing to do with GUIs, like
some homegrown Exception classes.

You can get rid of most of these simply by not extending these mostly
concrete classes (okay, difficult for JApplet, but you can limit the
number of subclasses, Throwable is quite useful too...).
Can someone explain what this message is about: what it means, why I get it,
and how I can prevent it?

When an object is serialised, the class information includes this UID.
If you choose not to provide one, then a value is derived using some
function applied to the public attributes of the class. The message just
mean that that function will need to be applied to you class if an
instance is serialised. You can supply one by adding a field:

private static final long serialVersionUID = ...some long number...L;
I've read the article about Serializable in the API but most of it goes
right over my head. I'm particularly foggy on which value I'm supposed to
use in the serialVersionUID constant if I choose to code one, which is
apparently highly-recommended.

You can choose anything. Preferably it should not duplicate the number
of another class. One way of creating it is to run serialver on the
class, and that gives the number which would be assigned anyway. If the
way the class is serialised changes, then so should the serialVersionUID.
I'm also unclear on what Bad Things are going to happen if I don't change my
code to prevent this warning.

The Bad Thing is that a change in public attributes of a class may
change the generated UID, and therefore prevent new code loading older
serialised data, or vice versa.

The Bad Thing that may happen if you do supply one is that you really do
change the serialised format, but not the UID. Hence a complete mess
when it comes to deserialisation.

It is a pain if you want a custom exception, say, and have no intention
of going anywhere near serialization. I guess you could get a revision
control system to embed a version number in the constant.

Tom Hawtin
 
R

Rhino

Thomas Hawtin said:
You can get rid of most of these simply by not extending these mostly
concrete classes (okay, difficult for JApplet, but you can limit the
number of subclasses, Throwable is quite useful too...).
Sorry, I don't follow you. If I want to have the benefit of all the existing
code with JFrame and its parents, how do I get them without subclassing
JFrame?

Also, are you suggesting that in cases where I extend a given Exception, I
extend Throwable instead?
When an object is serialised, the class information includes this UID. If
you choose not to provide one, then a value is derived using some function
applied to the public attributes of the class. The message just mean that
that function will need to be applied to you class if an instance is
serialised. You can supply one by adding a field:

private static final long serialVersionUID = ...some long number...L;
Why is it better for me to specify my own serialVersionUID than let the
function be applied for me? Obviously, I get a bit more control but I'm not
sure what that buys me in this case?
You can choose anything. Preferably it should not duplicate the number of
another class. One way of creating it is to run serialver on the class,
and that gives the number which would be assigned anyway. If the way the
class is serialised changes, then so should the serialVersionUID.
What is 'serialver' and how/when do I run it, i.e. is it something I need to
do when I compose the code?

What do you mean by "if the way the class is serialized changes"? What are
the different ways a class can be serialized? Why would the way it is
serialized at one point in time change in the future?
The Bad Thing is that a change in public attributes of a class may change
the generated UID, and therefore prevent new code loading older serialised
data, or vice versa.
"Older serialized data"? Could you please explain this scenario to me? I
would have thought that I was always going to be reading _current_ data, not
"older" data. Older data suggests non-current data, something that was
stored in a file or database perhaps, but has become superseded, like a
former salary.
The Bad Thing that may happen if you do supply one is that you really do
change the serialised format, but not the UID. Hence a complete mess when
it comes to deserialisation.
How would I change the serialised format? Is that something I'd do
consciously? If so, why would I do it? Or does it have the potential to
change on me without any conscious intent on my part, say due to a compiler
change?
It is a pain if you want a custom exception, say, and have no intention of
going anywhere near serialization. I guess you could get a revision
control system to embed a version number in the constant.
As you can probably see from my questions, I'm really quite lost. Perhaps I
need to get a few concepts explained to me before I will understand this
discussion. Can you recommend a place to get those concepts, whatever they
are?

I'm afraid my OO theory is not particularly strong and that may be getting
in the way here....

Rhino
 
T

Thomas Hawtin

Rhino said:
Sorry, I don't follow you. If I want to have the benefit of all the existing
code with JFrame and its parents, how do I get them without subclassing
JFrame?

JFrame frame = new JFrame();

Sorted.
Also, are you suggesting that in cases where I extend a given Exception, I
extend Throwable instead?

No! I used Throwable, as Throwable is the base class of Exception that
implements Serializable.
Why is it better for me to specify my own serialVersionUID than let the
function be applied for me? Obviously, I get a bit more control but I'm not
sure what that buys me in this case?

Just in case the class changes slightly, say an extra method. Then the
UID changes and serialisation interoperability fails.
What is 'serialver' and how/when do I run it, i.e. is it something I need to
do when I compose the code?

It's a program in the JDK.
What do you mean by "if the way the class is serialized changes"? What are
the different ways a class can be serialized? Why would the way it is
serialized at one point in time change in the future?

A class can store serialised data as it likes. For instance, you can
store a huge number as an array of bytes. Then you might want to change
to using an array of int. Without code to detect which is which,
deserialising the wrong form will fail. In general you might just get
corrupt data and no exception.
"Older serialized data"? Could you please explain this scenario to me? I
would have thought that I was always going to be reading _current_ data, not
"older" data. Older data suggests non-current data, something that was
stored in a file or database perhaps, but has become superseded, like a
former salary.

Data serialised by older classes.
How would I change the serialised format? Is that something I'd do
consciously? If so, why would I do it? Or does it have the potential to
change on me without any conscious intent on my part, say due to a compiler
change?

Just changing the name of a field would do it.

Tom Hawtin
 
R

Rhino

Roedy Green said:
Okay, I read through this article and got somewhat more of a grasp on how
serialization works.

I'm still not clear why I need to get into all of this rigamarole though.

I've got many classes that are based on serializable classes like JFrame but
I don't have any desire whatever to send my classes over sockets or write
them to the file system. Obviously, the source code and compile class will
need to be on the file system but I don't see why my runtime instances of
these classes will need to be sent over sockets or written to the file
system. So what's my best strategy for dealing with serializability? I'm
strongly tempted to disable the compiler warnings for this kind of warning
but I suppose I'd still rather modify the code so that the warnings go away
altogether. What is my best plan for accomplishing that?

Rhino
 
R

Rhino

Thomas Hawtin said:
JFrame frame = new JFrame();

Sorted.


No! I used Throwable, as Throwable is the base class of Exception that
implements Serializable.


Just in case the class changes slightly, say an extra method. Then the UID
changes and serialisation interoperability fails.


It's a program in the JDK.


A class can store serialised data as it likes. For instance, you can store
a huge number as an array of bytes. Then you might want to change to using
an array of int. Without code to detect which is which, deserialising the
wrong form will fail. In general you might just get corrupt data and no
exception.


Data serialised by older classes.


Just changing the name of a field would do it.

I really appreciate your attempt to clarify this issue for me but I'm just
not following you. I'm sure it's my fault, not yours.

I read the article Roedy cited but I still don't see the big picture with
regards to serialization. Maybe Roedy can clear this up for me.... (See my
followup to Roedy if you like.)

Rhino
 
M

Mike Schilling

Thomas Hawtin said:
When an object is serialised, the class information includes this UID. If
you choose not to provide one, then a value is derived using some function
applied to the public attributes of the class. The message just mean that
that function will need to be applied to you class if an instance is
serialised. You can supply one by adding a field:

private static final long serialVersionUID = ...some long number...L;
You can choose anything. Preferably it should not duplicate the number of
another class.

Why does this matter? So far as I know, there's no chance that a class's
serialVersionUID will be used for a class with a different fully-qualified
name. In fact, when I use serializeable classes (which is rarely) I set
serialVersionUID to 1 originally, and increment it if I ever break
compatibility.
 
M

Mike Schilling

Rhino said:
Okay, I read through this article and got somewhat more of a grasp on how
serialization works.

I'm still not clear why I need to get into all of this rigamarole though.

I've got many classes that are based on serializable classes like JFrame
but I don't have any desire whatever to send my classes over sockets or
write them to the file system. Obviously, the source code and compile
class will need to be on the file system but I don't see why my runtime
instances of these classes will need to be sent over sockets or written to
the file system. So what's my best strategy for dealing with
serializability? I'm strongly tempted to disable the compiler warnings for
this kind of warning but I suppose I'd still rather modify the code so
that the warnings go away altogether. What is my best plan for
accomplishing that?

Since serializability is indicated by implementing Serializeable, your
classes are serializeable whether you like it or not, hence the warning. In
your shoes, I would probably have the following two conventions for all such
classes:

1. Set serialVersionUID to -1.
2. Implement private void writeObject(ObjectOutputStream out) and private
void readObject(ObjectInputStream in) to throw exceptions e.g. "class not
serializeable".

The second comes from my dislike of leaving booby-traps for the unwary.
Since you're not designing your classes to be serializeable, make sure no
one tries to use them that way.
 
R

Roedy Green

I've got many classes that are based on serializable classes like JFrame but
I don't have any desire whatever to send my classes over sockets or write
them to the file system.

Why did you make them serializable then? That's what the
Serialializable interface is for. If you are extending Serializable
classes, the author wanted those classes to be Serializable so that
could be used to save state or with RMI.
 
T

Thomas Hawtin

Mike said:
Why does this matter? So far as I know, there's no chance that a class's
serialVersionUID will be used for a class with a different fully-qualified
name. In fact, when I use serializeable classes (which is rarely) I set
serialVersionUID to 1 originally, and increment it if I ever break
compatibility.

I believe it is permitted for the deserialised instance to have a
different class name to the one in the stream. The SUIDs must match (and
if it is a mistake then they really shouldn't).

Different serialVersionUIDs may be able to detect renaming issues better.

An implementation may get better performance looking up on SUID before
class name, although clearly not with classes under your scheme.

Tom Hawtin
 
T

Thomas Hawtin

Roedy said:
Why did you make them serializable then? That's what the
Serialializable interface is for. If you are extending Serializable
classes, the author wanted those classes to be Serializable so that
could be used to save state or with RMI.

java.awt.Component implements java.io.Serializable...

Tom Hawtin
 
M

Mike Schilling

Thomas Hawtin said:

I consider it the sign of a far braver man than I :)

Though the javadoc here says "Load the local class equivalent of the
specified stream class description. Subclasses may implement this method to
allow classes to be fetched from an alternate source." It also calls this
the obverse of ObjectOutputStream.annotateClass(), which says

Subclasses may implement this method to allow class data to be stored in the
stream. By default this method does nothing. The corresponding method in
ObjectInputStream is resolveClass. This method is called exactly once for
each unique class in the stream. The class name and signature will have
already been written to the stream. This method may make free use of the
ObjectOutputStream to save any representation of the class it deems suitable
(for example, the bytes of the class file). The resolveClass method in the
corresponding subclass of ObjectInputStream must read and use any data or
objects written by annotateClass.

So my guess is that this is intended to find the correct version of the
named class rather than to substitute a class of a different name.
 
J

J. Verdrengh

Can someone explain what this message is about: what it means, why I get
it, and how I can prevent it?

An instance of a serializable class can be serialized: this means that its
state can be saved to a stream.
When you save an instance of a serializable class Test, you can reload the
instance into memory at a later point in time. The field serialVersionUID is
used to verify whether the class Test hasn't changed since you saved the
instance.

You can generate a number for a .class file: try
"$JAVA$/bin/serialver.exe -show" where $JAVA$ is your java-path.

Then put the generated value in the corresponding .java file like this:
private final long serialVersionUID = generatedValue;

Whenever you change some instance fields of the class, the generated value
will change (and you are supposed to adapt the value of serialVersionUID).
This way, when you try to load an old stream into an instance of the new
(adapted) class, an Exception will be thrown because serialVersionUID of the
saved stream doesn't match serialVersionUID of the adapted class.



Question to the community: why doesn't Java automatically save some
hashvalue when streaming an instance of a serializable class instead of
relying on a 'manual' value like serialVersionUID?
 
M

Mike Schilling

J. Verdrengh said:
Question to the community: why doesn't Java automatically save some
hashvalue when streaming an instance of a serializable class instead of
relying on a 'manual' value like serialVersionUID?

That is, in fact, the default behavior. But you, the developer, might
consider two versions of the class compatible even though their hash would
differ, e.g.

"I added two fields, but they're null by default anyway, so I can still
deserialize older versions safely."
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,769
Messages
2,569,580
Members
45,054
Latest member
TrimKetoBoost

Latest Threads

Top