How are incompatible classes with same serialVersionUID handled? [duplicate] - java

I have a class that is serialised. Now I need to add a new variable into the class, with setter and getter methods. This class is sent over wire in RMI.
Without changing the UID, can I add new parameters and getter and setter methods for it? I tried to write an example class that is sent over wire, and did not change the UID, and added new parameters and getter and setter methods for it. On the other end, I tested it and I still got the values properly. I had assumed, if I add new parameters, getter and setter methods, I need to change the UID. Am I wrong?

If you hard-code the SerialVersionUID of a class, (to 1L, usually), store some instances, and then re-define the class, you basically get this behavior (which is more or less common sense):
New fields (present in class definition, not present in the serialized instance) are assigned a default value, which is null for objects, or the same value as an uninitialized field for primitives.
Removed fields (not present in class definition but present in the serialized instance) are simply ignored.
So the general rule of thumb is, if you simply add fields and methods, and don't change any of the existing stuff, AND if you're OK with default values for these new fields, you're generally OK.

Wow, a lot of bad information.
Java serialization is +very+ robust. There are a very well defined set of rules governing backwards compatibility of objects with the same uid and different data. the basic idea is that as long as you don't change the the type of an existing member, you can maintain the same uid without data issues.
that said, your code still needs to be smart about handling classes with potentially missing data. the object may deserialize correctly, but there may not be data in certain fields (e.g. if you added a field to the class and are deserializing an old version of the class). if your code can handle this, than you can probably keep the current uid. if not, then you should probably change it.
in addition to the pre-defined rules, there are advanced usage scenarios where you could even change the type of existing fields and still manage to deserialize the data, but that generally only necessary in extreme situations.
java serialization is very well documented online, you should be able to find all this information in the relevant sun/oracle tutorials/docs.

This only matters if you let Java generate a default UID for your class. It uses the actual members and methods of the class to generate it, thus making it invalid once you change the class structure. If you provide an UID for your class then this only matters if you need to deserialize older versions of your class from a file and such.

Want to define few point to highlight the changes which impacts serialization.
Below you will find the link to Oracle Java Docs for more details.
Incompatible Changes
Incompatible changes to classes are those changes for which the guarantee of interoperability cannot be maintained. The incompatible changes that may occur while evolving a class are:
Deleting fields
Moving classes up or down the hierarchy
Changing a nonstatic field to static or a nontransient field to transient
Changing the declared type of a primitive field
Changing the writeObject or readObject method so that it no longer writes or reads the default field data or changing it so that it attempts to write it or read it when the previous version did not.
Changing a class from Serializable to Externalizable or vice versa.
Changing a class from a non-enum type to an enum type or vice versa.
Removing either Serializable or Externalizable.
Adding the writeReplace or readResolve method to a class, if the behavior would produce an object that is incompatible with any older version of the class.
Link from where the above information is taken
http://docs.oracle.com/javase/7/docs/platform/serialization/spec/version.html#6678

Related

What is the difference between a final Class and a Record?

In simple words what is the difference between a final class and a record in Java 17?
In which case should I use a record?
Record is an immutable class, i.e. all its fields are final. Records are implicitly final, hence as well as regular final class record can't be extended.
There are a number of restrictions imposed on records (for more details, take a look at JEP 395).
Contrary to normal classes:
it's forbidden to declare instance fields explicitly inside records (and reminder: all fields are final, which is a very impotent distinction);
extends clause is not allowed with records, because every record implicitly extends abstract class Record;
record can't be declared with any of these modifiers: abstract, sealed, or non-sealed (as a consequence of being implicitly final);
records can't declare instance initializers and native methods.
Records are meant to be "transparent carriers for immutable data" as JEP 395 says.
They are designed to be concise, default constructor, getters, hashCode/equals and toString() will be generated by the compiler for you. So that inside a record you need to declare only your custom logic (if any) and record declaration can be literally a one-liner.
Records differ a lot from regular final classes.
Also, apart from the peculiarities mentioned above, the mechanism of serialization / deserialization was reimplemented for records, so that deserialization doesn't bypass the constructor.
In which case should I use a record?
In short, if your objects must be stateful, or you need to extend a particular class, then you can't utilize record in such a case.
On the other hand, if your objects are meant just carry the data, they are not intended to be modified or inherit from other classes, then it might be a good candidate to be implemented as a record.
A final class is simply one that cannot be extended. But that imposes no other constraints on the class; it can still have mutable fields, fully encapsulate its state, etc.
A record is a transparent carrier for a given tuple of state components, and is required to expose an API derived from its state description. Records are therefore more heavily constrained, but in exchange for these constraints, you get a great deal of convenience (constructors, accessors, Object methods) as well as some semantic promises (e.g., that unpacking a record with its component accessors and repacking the results with the constructor gives you an equals object.)
Records also happen to be final classes, but this is a very small part of what it means to be a record.

Serialization - Manually defining a SerialVersionUID required? [duplicate]

This question already has answers here:
What is a serialVersionUID and why should I use it?
(25 answers)
Closed 8 years ago.
I'm currently trying to justify the removal of the rule in findbugs/sonar that classifies making a class serializable, or in most of our cases, extending our base class that makes the classes serializable without the dev realising, then not defining a SerialVersionUID a critical error!
This of course makes our muggle management (read management that are not developers) panic, thinking we have critical bugs that must be fixed!
My argument is that in the current versions of Java, you do not need to provide a SerialVersionUID as the JVM will do that for you in cases where you serialize a class, but can anybody point any reasons why in todays day and age we should still provide a UID ourselves rather than leave it to the JVM? Looking on the web, the only reasoning I can really find for providing a UID nowadays is that not doing it is 'bad practice'.
Any ideas?
SerialVersionUID is used to check whether the version of class used for serializing and de-serializing is same or not across different JVM's.The default value generated by Serializable Runtime is sensitive to class details.So,although the classes loaded across different JVM may be compatible,but still you can get a false InvalidClassException .
Check the 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. If the receiver has loaded a class for the object that has a different serialVersionUID than that of the corresponding sender's class, then deserialization will result in an InvalidClassException. A serializable class can declare its own serialVersionUID explicitly by declaring a field named "serialVersionUID" that must be static, final, and of type long:
*ANY-ACCESS-MODIFIER static final long serialVersionUID = 42L;
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. Therefore, to guarantee a consistent serialVersionUID value across different java compiler implementations, a serializable class must declare an explicit serialVersionUID value. It is also strongly advised that explicit serialVersionUID declarations use the private modifier where possible, since such declarations apply only to the immediately declaring class--serialVersionUID fields are not useful as inherited members. Array classes cannot declare an explicit serialVersionUID, so they always have the default computed value, but the requirement for matching serialVersionUID values is waived for array classes.*
I wanted to extend my comment but ran out of room.
nb These aren't my original thoughts, but rather from Joshua Bloch's Effective Java
Reason 1: Serialized objects can persist
Even small, otherwise trivial changes to the class will cause the JVM to generate a different ID. So when you try to deserialize an object serialized with an older but otherwise compatible class structure, the result is an InvalidClassException.
Changing something as innocuous as adding a convenience accessor to a class will force a different UID to be computed. Similarly, one of the things that influence the generated UID are private members. So not only are you restricted from changing the public facing API (which may be preferable), but you're also restricted from changing too drastically any private implementation details, for risk of causing a UID mismatch.
The other way to look at this is that by manually defining an UID, while you can ensure that the JVM will attempt to deserialize an object with its intended class, regardless of the changes to your class, you can also prevent the JVM from attempting to deserialize an object with its intended class (eg your new class is incompatible) by changing this UID.
Reason 2: Runtime computation is more expensive
Computed UIDs are computed at runtime. Manually specifying this field obviates this computation.
The central purpose of defining serialVersionUID is to control serialization compatibility. As other answers and the documentation has noted, unless a specific value is declared, the value is computed from a variety of class characteristics, even ones that don't actually affect the serialized form, such as the signatures of public methods. If you don't provide a serialVersionUID, and one or more of these characteristics of the class differs between serialization and deserialization, an InvalidClassException will be thrown.
Now to the question of when one should or should not declare a serialVersionUID.
If you care about serialization compatibility, you should almost always declare a serialVersionUID. Doing this is the only possible way to evolve the class and have the serialized form be compatible with other versions of the class. You will probably also have to provide custom readObject and writeObject methods and use various mechanisms like readFields, putFields, and serialPersistentFields to control the serialized format, and to cope with potential changes to the serialized format.
By "care about serialization compatibility", suppose you've serialized an object and have stored it in a file or database. Do you expect to have future versions of your system (with evolved versions of the classes) be able to read the stored serialized objects? Or, suppose you serialize objects and send them over a network to other applications that deserialize them. This happens in RMI, or it could happen if you develop your own network protocol that sends and receive serialized objects. If so, then on your network, can you have different versions of your application running in different places on the network, and do you expect them to be able to talk to each other successfully?
If any of the above are true, you care about serialization compatibility, and you need to declare serialVersionUID.
There are times when you might care about serialization compatibility but when it doesn't make sense to declare a serialVersionUID. One example is with anonymous inner classes. Such a class can be made serializable, but it's impractical to try to make it compatible, for several reasons. Anonymous inner classes have compiler-generated names that are implementation specific. They can also change across recompiles. AICs also contain references to their enclosing instance and references to any objects that might be captured from the local scope. All of these objects, and their transitive closure, become part of the serial form of an AIC. For these reasons it's a bad idea to serialize AICs, let alone try to achieve serial compatibility for them. In such cases, adding a serialVersionUID is just a distraction. If you are tempted to serialize AICs, you probably want to restructure the code to serialize something else instead.
There might be times when you don't care about serialization compatibility of different class versions at all.
One example is if you have a closely coupled set of JVMs that are all sharing classes from the same classpath, and they're exchanging serialized objects. Since they are using the same actual classes, there can't be any incompatibility. Declaring a serialVersionUID for classes in this case is useless busywork. In fact, doing so may conceal errors. In such a multi-JVM scheme, if there is a serialization compatibility error, that indicates some kind of configuration problem, since it means the JVMs are using different classes. You'd want that to be detected as soon as possible, and not declaring serialVersionUID would cause an error to be manifested more quickly.
Another reason is that Serializable is inherited, which may cause classes down the inheritance tree to become Serializable even if they are never intended to be serialized. Again, declaring serialVersionUID for such classes is useless busywork. There's no formal way for a class to reject its inheritance and "undeclare" serializability. Best practice, though, is for such classes to implement readObject and writeObject and for them to unconditionally throw an exception like InvalidObjectException or NotSerializableException.
Still another example is that your product requirements (or whatever) might simply decide not to care about serialization compatibility in certain cases or not at all. It's something that you might decide is just "not supported." The JDK itself has taken this approach. In general, most public, serializable classes in the JDK are constrained to be forward and backward serialization compatible. As such, all of these classes declare a serialVersionUID and take care to deal with missing or added fields. However, some portions of the JDK, most notably AWT and Swing, are explicitly not serialization compatible across releases. Such classes have a disclaimer that warns of serial incompatibility, and instead of declaring a serialVersionUID, these classes include an annotation #SuppressWarnings("serial") to eliminate the warnings.
Bottom line is that it's a mistake to slavishly declare serialVersionUID in every class that happens to inherit Serializable. There are good reasons to declare it, and there are also good reasons not to declare it. You should decide explicitly.

ObjectOutputStream doesn't correctly save fields of superclasses unless they're Serializable, why no error or warning?

Say I have an ArrayList<B> array of objects from a certain class B, which extends A. B has an instance field bb and A a field aa. I know that saving array to a .dat-file using ObjectOutputStream requires that B (not just ArrayList!) implement Serializable. I've found, however, that when loading the object back from the file (using an ObjectInputStream):
arrayLoaded = (ArrayList<B>)myObjIn.readObject();
the loaded array isn't identical to the original array: In the particular case, arrayLoaded.get(0).bb has the same value as in array, but arrayLoaded.get(0).aa is "zeroed". It has a default initialization value, regardless of its value when array was saved to file. However, this problem is solved by letting also A implement Serializable.
What bothers me is that this error is so subtle: no exception, no warning (in eclipse), nothing. Is there a reason for this or is this simply an oversight by the java developers? Do I just have to accept it and think hard about which classes in the hierarchy implement Serializable every time I want to use object IO streams?
Just because B implements Serializable, that does not retroactively include the fields of the non-serializable superclass in what gets serialized. (This makes sense, especially when you consider that being able to serialize private and package-private fields of any class just by extending it and implementing Serializable would violate its encapsulation.)
A field declared in A will behave the same as a field declared as transient in B. There is a workaround however. From the documentation for Serializable:
To allow subtypes of non-serializable classes to be serialized, the
subtype may assume responsibility for saving and restoring the state
of the supertype's public, protected, and (if accessible) package
fields. The subtype may assume this responsibility only if the class
it extends has an accessible no-arg constructor to initialize the
class's state.
So you will need to implement writeObject and readObject in B to handle the serialization/deserialization of A.aa.
What bothers me is that this error is so subtle: no exception, no warning (in eclipse), nothing. Is there a reason for this or is this simply an oversight by the java developers?
It is by design. (See #Paul Bellora's answer). The alternatives would be to:
Make it illegal to declare a class Serializable unless its superclass is Serializable. That's obviously unworkable.
Automatically serialize the superclasses fields which breaks if the should or can't be serialized. (Note that we can't rely on transient here, because if the designer of the superclass didn't intend the it to be serializable, he/she won't have labelled the fields.)
Do I just have to accept it and think hard about which classes in the hierarchy implement Serializable every time I want to use object IO streams?
Basically, yes. In particular, you need to think hard when you write a Serializable subclass of an existing non-Serializable class.
I guess it is possible to write FindBugs / PMD / etc rules to flag this particular usage as potentially problematic.

Java serialization - java.io.InvalidClassException local class incompatible [duplicate]

This question already has answers here:
java.io.InvalidClassException: local class incompatible:
(10 answers)
Closed 3 years ago.
I've got a public class, which implements Serializable, that is extended by multiple other classes. Only those subclasses were ever serialized before - never the super class.
The super class had defined a serialVersionUID.
I'm not sure if it matters, but it was not marked private, but rather it just had the default protection - you might say it was package protected
static final long serialVersionUID = -7588980448693010399L;
The super class, nor any of the subclasses, however implemented readObject or writeObject, and none of the subclasses had an explicitly defined serialVersionUID. I figured one defined in the superclass would be sufficient.
Despite all this, things were fine as far as reading back previously serialized objects until a new instance variable, a List/ArrayList, along with a new method was added to the super class, and some private instance variables were added to one of its subclasses.
Now when trying to read back previously serialized objects, an exception is being thrown. One similar to this:
com.SomeCompany.SomeSubClass; local class incompatible: stream classdesc serialVersionUID = 1597316331807173261, local class serialVersionUID = -3344057582987646196
I'm assuming this is caused because the default serialVersionUID, which was used because I didn't declare one in any of the subclasses, has now changed due to the changes in the superclass and one subclass.
Suggestions on how to get out of this dilemma would be appreciated. I'm assuming I need to implement readObject and writeObject, but other than invoking defaultReadObject() and defaultWriteObject(), I'm not exactly sure what I need to do. Nor do I know if I need to add serialVerisonUIDs to all of the subclasses or if readObject and writeObject need to be implemented by each subclass, or if I can just implement them once, assuming I need to at all, in the superclass.
#DanielChapman gives a good explanation of serialVersionUID, but no solution. the solution is this: run the serialver program on all your old classes. put these serialVersionUID values in your current versions of the classes. as long as the current classes are serial compatible with the old versions, you should be fine. (note for future code: you should always have a serialVersionUID on all Serializable classes)
if the new versions are not serial compatible, then you need to do some magic with a custom readObject implementation (you would only need a custom writeObject if you were trying to write new class data which would be compatible with old code). generally speaking adding or removing class fields does not make a class serial incompatible. changing the type of existing fields usually will.
Of course, even if the new class is serial compatible, you may still want a custom readObject implementation. you may want this if you want to fill in any new fields which are missing from data saved from old versions of the class (e.g. you have a new List field which you want to initialize to an empty list when loading old class data).
The short answer here is the serial ID is computed via a hash if you don't specify it. (Static members are not inherited--they are static, there's only (1) and it belongs to the class).
http://docs.oracle.com/javase/6/docs/platform/serialization/spec/class.html
The getSerialVersionUID method returns the serialVersionUID of this
class. Refer to Section 4.6, "Stream Unique Identifiers." If not
specified by the class, the value returned is a hash computed from the
class's name, interfaces, methods, and fields using the Secure Hash
Algorithm (SHA) as defined by the National Institute of Standards.
If you alter a class or its hierarchy your hash will be different. This is a good thing. Your objects are different now that they have different members. As such, if you read it back in from its serialized form it is in fact a different object--thus the exception.
The long answer is the serialization is extremely useful, but probably shouldn't be used for persistence unless there's no other way to do it. Its a dangerous path specifically because of what you're experiencing. You should consider a database, XML, a file format and probably a JPA or other persistence structure for a pure Java project.
For me, I forgot to add the default serial id.
private static final long serialVersionUID = 1L;
This worked for me:
If you wrote your Serialized class object into a file, then made some changes to file and compiled it, and then you try to read an object, then this will happen.
So, write the necessary objects to file again if a class is modified and recompiled.
PS: This is NOT a solution; was meant to be a workaround.

Why is this class not Serializable?

I was using the Mersenne-Twister implementation at http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/VERSIONS/JAVA/MTRandom.java as a drop-in replacement for the default java.util.Random class. However, four fields (an int, a boolean and two byte[]) are marked as transient. This means that I can't serialize an object of this class without implementing custom functionality.
The question is, is there any reason that these fields are marked transient? Is there any code in there that holds information that won't make any sense when the object is read in from a file? I removed the transient modifier from the fields and it seems to work fine, but I haven't tested it intensively and so might there be cases where it breaks?
Personally, I can't see why, since all that's done in the class is arithmetic.
From the comment on serialVersionUID, it looks like the author didn't want to consider serialisation. Adding transient may have suppressed some compiler/IDE warnings.
Most likely the reasoning behind making all of the non-static fields of the class transient was so that the MTRandom class stays binary compatible with java.util.Random, from which it is extended.
So theoretically, you could serialize an MTRandom instance, and deserialize it as a Random instance and everything would work.
If those fields aren't transient, then they would be serialized and become incompatible.
However, as far as I can tell, removing the transients shouldn't cause a problem for you.

Categories