Sitelet https://howtodoinjava.com/java/serialization/java-serialization/#comment-121136

Java Serialization with Serializable and serialVersionUID

Learn how to write Java objects to bytes and read them back with ObjectOutputStream and ObjectInputStream. See how serialVersionUID, transient fields, records and filters change the result.

java 8

Java serialization turns an object into bytes, and deserialization reads those bytes back into a new object with the same field values. A class allows serialization by implementing the Serializable interface.

We use serialization to save an object to a file or to send it over a socket, for example when an app server keeps user sessions across a restart.

The following example writes a Recipe with ObjectOutputStream.writeObject() and reads it back with ObjectInputStream.readObject(), with the result of each line as a comment.

record Recipe(String name, int minutes) implements Serializable { }

// 1. Serialize: object -> bytes in a file
try (var out = new ObjectOutputStream(new FileOutputStream("recipe.ser"))) {
  out.writeObject(new Recipe("Pasta", 20));                // recipe.ser = 114 bytes
}

// 2. Deserialize: bytes -> a new object
try (var in = new ObjectInputStream(new FileInputStream("recipe.ser"))) {
  Recipe copy = (Recipe) in.readObject();                  // copy = Recipe[name=Pasta, minutes=20]
  boolean same = copy.equals(new Recipe("Pasta", 20));     // true
}

// 3. Every field must be serializable too
record MealPlan(String day, MenuItem item) implements Serializable { }   // MenuItem is not Serializable
new ObjectOutputStream(OutputStream.nullOutputStream())
    .writeObject(new MealPlan("Monday", new MenuItem("Salad")));
                       // NotSerializableException: com.howtodoinjava.core.serialization.MenuItem

Notice that the copy equals the original, and that a field of a non-serializable type fails at runtime, not at compile time.

Next, we look at what Java writes into the bytes and how a version number decides whether an old file can still be read. We also cover special cases such as transient fields, records, inheritance and singletons, and finally why we never read serialized data from an unknown source and what to use instead.

1. What Is Java Serialization?

Java writes an object as bytes only when its class implements Serializable, which is a marker interface (that is, an interface with no methods).

The interface only tells the JVM that objects of this class may be written as bytes, while two classes from the java.io package do the actual work.

  • ObjectOutputStream.writeObject(obj) writes a short description of the class and the field values of obj. It also writes every object that obj points to, and all these connected objects together are called the object graph.
  • ObjectInputStream.readObject() reads the bytes, loads the class, and then creates a new object and fills its fields. The return type is Object, so we cast the result to our class.

Java uses serialization in several places. For example, Java RMI sends objects over the network with it, and application servers store user sessions with it. We can also use it to make deep copies of objects.

Reading always gives us a new object, and the constructor of a normal Serializable class does not run. Java copies the field values straight from the bytes into the new object.

Three boxes from top to bottom. A Cookbook object with title, recipes, a transient totalMinutes = 50 and a static publisher. writeObject writes a byte stream with the class descriptor (class name, serialVersionUID, field names and types) and the field values of the whole object graph. totalMinutes and publisher are not written. readObject creates a new Cookbook without calling its constructor. totalMinutes starts at 0, and the custom readObject sets it back to 50
writeObject() stores the class description and the field values. transient and static fields are not written.

2. Java Serialization Example

The following example is a small recipe app, and the complete code is in the Core-Java repository on GitHub. The class JavaSerializationDemo prints every result in the article, compiled for Java 21 and run on JDK 25.

2.1. Making a Class Serializable

A class can be serialized when the class, or one of its parent classes, implements Serializable, but that alone is not enough. Every field that Java writes must also hold a serializable value, otherwise writeObject() throws NotSerializableException at runtime.

String, the wrapper classes such as Integer, ArrayList, HashMap and List.of() are all serializable. Our MenuItem class is not, so a MealPlan that holds a MenuItem fails.

record MealPlan(String day, MenuItem item) implements Serializable { }

out.writeObject(new MealPlan("Monday", new MenuItem("Salad")));
// java.io.NotSerializableException: com.howtodoinjava.core.serialization.MenuItem

The exception names the class but not the field. To see the field too, we start the JVM with the option -Dsun.io.serialization.extendedDebugInfo=true, and the message shows the path from the root object to the bad field.

java.io.NotSerializableException: com.howtodoinjava.core.serialization.MenuItem
	- field (class "com.howtodoinjava.core.serialization.MealPlan", name: "item", type: "class com.howtodoinjava.core.serialization.MenuItem")
	- root object (class "com.howtodoinjava.core.serialization.MealPlan", MealPlan[day=Monday, item=com.howtodoinjava.core.serialization.MenuItem@443b7951])

We have two ways to fix the error. We either make MenuItem serializable, or we mark the field transient so that Java skips it when writing. The second way fits when we can rebuild the value later.

2.2. Writing and Reading a File

ObjectOutputStream and ObjectInputStream each wrap another stream, so the same code works for files, network sockets, byte arrays and any other stream. We open the streams in a try-with-resources block, which closes the stream even when an exception occurs. The method readObject() can throw two checked exceptions.

  • IOException when the bytes are broken or do not match the class.
  • ClassNotFoundException when the class named in the bytes is not on the classpath.
var bytes = new ByteArrayOutputStream();
try (var out = new ObjectOutputStream(bytes)) {
  out.writeObject(new Recipe("Pasta", 20));
}

try (var in = new ObjectInputStream(new ByteArrayInputStream(bytes.toByteArray()))) {
  Recipe copy = (Recipe) in.readObject();       // Recipe[name=Pasta, minutes=20]
}

Files with serialized objects often end in .ser, but the name is only a habit and Java does not require it. Serializing an ArrayList works the same way, as long as every element in the list is serializable.

2.3. Shared References in an Object Graph

In the next example, a list holds the same soup object twice. Java writes the soup object only once, and for the second entry it writes a short reference back to the first one. So the copied list also holds one Soup object twice, not two different objects.

Recipe soup = new Recipe("Soup", 30);
List<Recipe> week = new ArrayList<>(List.of(soup, soup));

List<Recipe> copy = (List<Recipe>) in.readObject();
boolean shared = copy.get(0) == copy.get(1);     // true  (one Soup object in the copy)
boolean original = copy.get(0) == soup;          // false (the copy is a new object)

The same rule handles circular references, where object A points to B and B points back to A. Java writes each object only once, so the writing does not loop forever.

3. transient and static Fields

Some fields should not go into the bytes, so we mark them with the transient keyword. Java skips a transient field when writing, and after reading, the field has its default value, such as null, 0 or false.

Java also skips static fields, because a static field belongs to the class and not to one object.

Our Cookbook class has one field of each kind. A transient field caches the total cooking time, and a static field holds the publisher name.

static String publisher = "Home Press";   // static: never written

private final String title;
private List<Recipe> recipes;
private transient int totalMinutes;       // transient: not written, recomputed in readObject()

Next, we write a cookbook to bytes and change the static field before we read the cookbook back.

byte[] data = toBytes(new Cookbook("Weeknight Meals", List.of(new Recipe("Pasta", 20), new Recipe("Soup", 30))));
Cookbook.publisher = "City Books";

Cookbook copy = (Cookbook) fromBytes(data);
String title = copy.getTitle();                // "Weeknight Meals"
List<Recipe> recipes = copy.getRecipes();      // [Recipe[name=Pasta, minutes=20], Recipe[name=Soup, minutes=30]]
int total = copy.getTotalMinutes();            // 50 (0 without the readObject() in section 6)
String publisher = Cookbook.publisher;         // "City Books" (the current class value, not the written one)

The title and the recipes come back as written, and the total is 50 only because of the custom readObject() method in section 6. The publisher shows the current value of the class, not the written value. The helper methods toBytes() and fromBytes() wrap the code of section 2.2, and both are in the SerializationUtil class of the example.

Mark a field transient when the field holds a computed value, such as a cache, or a resource such as a connection or a logger. Passwords and tokens are good candidates too, because Java does not encrypt the bytes.

4. serialVersionUID and InvalidClassException

Every serializable class has a version number called serialVersionUID, which is a long that Java writes into the bytes. When reading, readObject() compares the written number with the number of the class in the current JVM. If the two numbers differ, readObject() throws InvalidClassException. The check matters on the day we ship a new version of an app that still has to read the files saved by the old version.

4.1. What Happens Without a serialVersionUID

A class does not have to declare the number. In that case, Java computes it from the class name, the fields, the methods and the interfaces, so any change to the class gives a new number. For example, when we write a Pantry object to a file, add one field to the class and read the old file, readObject() throws InvalidClassException.

public class Pantry implements Serializable {
  String owner = "Lokesh";
  int shelves = 4;      // field added after the file was written
}
java.io.InvalidClassException: Pantry; local class incompatible: stream classdesc serialVersionUID = 8497059583825323096, local class serialVersionUID = 966084489768494949

The JDK tool serialver prints the computed number of a class, for example serialver -classpath target/classes Pantry. Different compilers can also compute different numbers for the same class, so a class without a declared number can fail even when nobody changed it.

4.2. Declaring serialVersionUID

Declare serialVersionUID in every serializable class from the first version, and change the number only when the new version cannot read the old data. The @Serial annotation, added in Java 14, asks the compiler to check that the field name and type are right.

@Serial
private static final long serialVersionUID = 1L;

With 1L in both versions of Pantry, the old file loads. The new field gets its default value 0, not 4, because no constructor runs, so the field initializer = 4 never runs either.

Pantry[owner=Lokesh, shelves=0]

We can also change the number on purpose. Reading old data then fails with the same exception, but the message shows our short numbers.

java.io.InvalidClassException: com.howtodoinjava.core.serialization.RecipeCard; local class incompatible: stream classdesc serialVersionUID = 2, local class serialVersionUID = 1

4.3. Compatible and Incompatible Changes

A matching serialVersionUID does not promise that reading works, because the number only tells Java to try. The versioning rules of the serialization specification decide whether the old bytes still fit the new class, and a compatible change is one where the new class can still read old data. The table assumes default serialization, meaning the class has no custom writeObject() or readObject().

Change to the classCompatible?What happens when reading old data
Add a fieldyesThe new field gets null, 0 or false. We can set a value in readObject() if needed
Change access modifiers of a fieldyesAccess has no effect on serialization
Change static to non-static, or transient to non-transientyesSame as adding a field
Add or remove writeObject()/readObject()yesExtra data is ignored. Missing data is read by default serialization
Add a class to the hierarchy, or add SerializableyesFields of the new class get default values
Delete a fieldno (formally)Old readers get a default value, which can break their logic
Change the type of a primitive field, for example int to longnoInvalidClassException, because the type in the bytes does not match
Change non-static to static, or non-transient to transientnoSame as deleting a field
Move a class up or down the hierarchynoThe data appears in the wrong order
Switch between Serializable and Externalizable, or between class and enumnoThe byte format is different
Stop calling defaultWriteObject()/defaultReadObject()noThe default field data must be always present or always absent

5. Serializing Records

A record is a short data class, such as our Recipe, and it is serializable when it implements Serializable. Java handles records differently from normal classes.

  • The bytes hold only the component values (the values listed in the record header, such as name and minutes). When reading, Java calls the canonical constructor, which takes all components, so the checks in the record’s constructor also run for data read from bytes.
  • Java ignores the methods writeObject(), readObject() and readObjectNoData() and the field serialPersistentFields in a record. The methods writeReplace() and readResolve() still work.
  • The serialVersionUID of a record is 0L unless we declare one, and Java does not compare the numbers for records. Instead, Java matches components by name, so a component added later gets its default value. For example, a Rating record read from older bytes gives Rating[recipe=Pasta, stars=0].

The next example shows why calling the constructor matters. Recipe rejects a negative cooking time in its constructor, and RecipeCard is a normal class with the same check in its constructor.

public record Recipe(String name, int minutes) implements Serializable {
  public Recipe {
    if (minutes < 0) {
      throw new IllegalArgumentException("minutes must be >= 0, but was " + minutes);
    }
  }
}

public class RecipeCard implements Serializable {
  public RecipeCard(String name, int minutes) {
    if (minutes < 0) { throw new IllegalArgumentException(/* same message */); }
    ...
  }
}

Say we write both objects to bytes and change the 20 in the bytes to -5, the way an attacker or a broken file could. The record fails with the constructor’s message, whereas the class loads the bad value because its constructor never runs.

Object recipe = fromBytes(tamperedRecipe);
// java.io.InvalidObjectException: minutes must be >= 0, but was -5
// cause: java.lang.IllegalArgumentException: minutes must be >= 0, but was -5

Object card = fromBytes(tamperedRecipeCard);       // RecipeCard[name=Pasta, minutes=-5]  (no check ran)

For new data classes, a serializable record is the safer choice, because bad data cannot get past the constructor. A normal class needs a custom readObject() to get the same protection.

6. Custom writeObject() and readObject() Methods

A serializable class can declare two private methods, writeObject() and readObject(), and when they exist, Java calls them instead of its default logic.

Most classes first call defaultWriteObject() or defaultReadObject() to do the normal work, and then add their own steps. The methods have three typical uses.

  • Check the values after reading, because the bytes can hold any values.
  • Make defensive copies of mutable fields, as a constructor would. A defensive copy is a new copy of a list or another changeable object, so outside code cannot change our object later.
  • Rebuild transient fields such as caches.

The Cookbook class does all three in its custom serialization methods.

@Serial
private void writeObject(ObjectOutputStream out) throws IOException {
  out.defaultWriteObject();               // writes title and recipes
}

@Serial
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
  in.defaultReadObject();                 // reads title and recipes
  if (title == null || title.isBlank()) {
    throw new InvalidObjectException("Cookbook title is missing");
  }
  recipes = new ArrayList<>(recipes);     // defensive copy
  totalMinutes = sum(recipes);            // rebuild the transient field
}

Bytes with a blank title fail when we read them.

java.io.InvalidObjectException: Cookbook title is missing

Treat readObject() like a public constructor and check every rule the constructor checks. Both methods must be private and have these signatures, otherwise Java ignores them without any warning. With the @Serial annotation and the -Xlint:serial compiler option, the compiler reports a wrong signature. Java calls both methods at a fixed point of the deserialization process.

7. Serializable vs Externalizable

With Externalizable, which extends Serializable, the class controls the whole byte format. The class writes every field itself in writeExternal() and reads the fields back in readExternal() in the same order, while Java adds only the class name and the version.

public Ingredient() { }                   // required: public no-arg constructor

@Override
public void writeExternal(ObjectOutput out) throws IOException {
  out.writeUTF(name);
  out.writeInt(grams);
}

@Override
public void readExternal(ObjectInput in) throws IOException {
  name = in.readUTF();                    // same order as writeExternal()
  grams = in.readInt();
}

// copy = Ingredient[name=flour, grams=500]

The public no-arg constructor is required. Without it, writing works, but reading fails with java.io.InvalidClassException: ExtNoCtor$Tag; no valid constructor. The differences between Externalizable and Serializable go beyond the constructor.

SerializableExternalizable
Methods to implementNone (marker interface)writeExternal() and readExternal()
Who writes the fieldsJava, by reflectionThe class, field by field
Constructor on readingNo-arg constructor of the first non-serializable parentPublic no-arg constructor of the class
transient keywordRespectedHas no effect. The class decides what to write
final fieldsSupportedNot possible, because readExternal() must assign them
Typical useDefault choiceCompact or hand-tuned formats, rarely needed today

8. Inheritance and Serialization

A child class inherits Serializable from its parent, so every subclass of a serializable class is serializable too.

A serializable child can also have a parent that is not serializable. In the example, Dessert is serializable but its parent MenuItem is not, so Java does not write the fields of MenuItem, and when reading, it calls the no-arg constructor of MenuItem.

public class MenuItem {                                   // not Serializable
  protected String name;
  public MenuItem() { this.name = "unknown"; }
  public MenuItem(String name) { this.name = name; }
}

public class Dessert extends MenuItem implements Serializable {
  private final int sugarGrams;
}

Object dessert = fromBytes(toBytes(new Dessert("Brownie", 30)));   // Dessert[name=unknown, sugarGrams=30]

The name Brownie is lost, because the MenuItem fields were not in the bytes and the MenuItem() constructor set the name to unknown. The parent may also have no no-arg constructor that the child can call, as our Drink extends MenuItemNoDefault shows. In that case, writing works, but reading fails.

java.io.InvalidClassException: com.howtodoinjava.core.serialization.Drink; no valid constructor

We can fix both cases in two ways. Either we make the parent Serializable, or the child writes and reads the parent fields by hand in its own writeObject() and readObject(). The image shows which constructor runs during readObject() for each kind of type.

Four rows. Serializable class Cookbook: only Object() runs, and the Cookbook constructor is skipped. Serializable child Dessert of plain parent MenuItem: MenuItem() runs and sets name to unknown. Without MenuItem(), reading fails with InvalidClassException. Record Recipe: the canonical constructor runs, so validation checks stream data. Externalizable Ingredient: the public no-arg constructor runs, then readExternal fills the fields
Only records and Externalizable classes run their own constructor during deserialization.

9. Singletons and readResolve()

A singleton is a class with only one object in the JVM. Because readObject() always creates a new object, the JVM holds two objects of the class after we read a singleton. We fix the second object with a private readResolve() method, which Java calls after reading, and the object it returns replaces the new copy.

public final class KitchenSettings implements Serializable {
  public static final KitchenSettings INSTANCE = new KitchenSettings();

  private KitchenSettings() { }

  @Serial
  private Object readResolve() {
    return INSTANCE;                      // drop the copy, return the one instance
  }
}

boolean noResolve = fromBytes(toBytes(KitchenSettingsNoResolve.INSTANCE)) == KitchenSettingsNoResolve.INSTANCE;   // false
boolean resolved = fromBytes(toBytes(KitchenSettings.INSTANCE)) == KitchenSettings.INSTANCE;                       // true

The method writeReplace() works the other way round. Before writing, it lets a class put a different object into the bytes, for example a small record.

An enum needs neither method, because for an enum constant Java writes only the name and returns the existing constant when reading. So an enum singleton is safe without extra code.

10. Deserialization Filters and Security

Never deserialize data from a source we do not trust. The bytes name the classes to create, and readObject() creates any serializable class on the classpath. Java also runs the readObject() and readResolve() methods of those classes, and all of this happens before our code sees the result.

Attackers combine these methods from common libraries into a gadget chain, which can run commands on the server. Deserialization of untrusted data is a major risk, and many remote code execution flaws in Java servers came from untrusted serialized data.

When an application must read serialized data anyway, we add a filter (a set of rules that limits what readObject() accepts). JEP 290 added ObjectInputFilter in Java 9, and JEP 415 added filter factories in Java 17.

10.1. A Filter for One Stream

A pattern filter is a list of rules separated by ;. Java checks the rules from left to right, and the first matching rule wins. In our filter, the first rule allows Recipe, the second allows classes of the java.base module, and the last rule, !*, rejects everything else. So a Recipe loads, and a Dessert is rejected.

ObjectInputFilter onlyRecipes = ObjectInputFilter.Config.createFilter(
    "com.howtodoinjava.core.serialization.Recipe;java.base/*;!*");

try (var in = new ObjectInputStream(new ByteArrayInputStream(data))) {
  in.setObjectInputFilter(onlyRecipes);
  Object result = in.readObject();
}
// Recipe  -> Recipe[name=Pasta, minutes=20]
// Dessert -> java.io.InvalidClassException: filter status: REJECTED

The rules use a few forms.

  • A class name allows the class, and a name that starts with ! rejects it.
  • The pattern pkg.* matches the classes of one package, and pkg.** also matches its subpackages.
  • The pattern module/* matches all classes of a module.

Filters can also limit the size of the data, which stops memory attacks that send huge arrays or very deep nesting.

ObjectInputFilter limits = ObjectInputFilter.Config.createFilter("maxdepth=5;maxarray=1000;maxbytes=10000");
// reading new int[5000] -> java.io.InvalidClassException: filter status: REJECTED

10.2. A Filter for the Whole JVM

The jdk.serialFilter system property sets one filter for the whole JVM. The filter applies to every ObjectInputStream that has no filter of its own, so streams inside libraries are covered too.

java -Djdk.serialFilter='com.howtodoinjava.core.serialization.Recipe;java.base/*;!*' FilterCheck
# Recipe[name=Pasta, minutes=20]
# java.io.InvalidClassException: filter status: REJECTED

We can also put the same property into the conf/security/java.security file of the JDK.

Our filter is an allow list, which names the accepted classes and ends with !*, whereas a block list names the rejected classes. An allow list is safer, because a block list misses dangerous classes that nobody has found yet. All pattern rules and the filter factory are part of serialization filtering in the JDK.

11. Alternatives to Java Serialization

Java serialization has three big limits, so for data exchange and storage, most projects use a format that is not tied to Java classes.

  • Only Java programs with the same classes can read the bytes.
  • The binary format is hard to read and debug.
  • The security problems from section 10 remain.

The two common alternatives are JSON and Protocol Buffers.

  • JSON with Jackson or Gson. JSON is readable text that every language supports, and Jackson creates only the classes and fields that we map.
  • Protocol Buffers (protobuf), a compact binary format. We describe the data in a .proto schema file, and a tool generates the classes. Protobuf has clear rules for adding and removing fields, so it fits gRPC services and high-volume messaging.

Jackson 3.2.3 writes and reads our record, and the record does not need to implement any interface.

JsonMapper mapper = JsonMapper.builder().build();

String json = mapper.writeValueAsString(new Recipe("Pasta", 20));   // {"name":"Pasta","minutes":20}
Recipe copy = mapper.readValue(json, Recipe.class);                 // Recipe[name=Pasta, minutes=20]

Of the three formats, only Java serialization needs a filter before it can read untrusted input.

Java serializationJSON (Jackson)Protocol Buffers
ReadersJava only, same classesAny languageAny language with generated code
FormatBinaryTextBinary
Schema evolutionserialVersionUID and the rules in 4.3Ignore or default unknown fieldsNumbered fields in the .proto file
Safe for untrusted inputNo, needs a strict filterYes, with default typing offYes
SetupNoneOne library.proto file and code generator

To turn an object into a text string, we serialize the object to a string.

12. Java Serialization FAQs

12.1. Is serialVersionUID Mandatory?

No. The code compiles and runs without the field, because Java computes a number. However, the computed number changes with almost any edit of the class, so old files then fail with InvalidClassException.

We declare private static final long serialVersionUID = 1L; in every serializable class. Records are the exception, because Java ignores the number for records.

12.2. Why Does Deserialization Not Call the Constructor?

Because the field values come from the bytes anyway, and the constructor may need arguments that the bytes do not have. So Java creates the object by running only the no-arg constructor of the first non-serializable parent, which is often Object.

Java then sets the fields from the bytes. Records and Externalizable classes work differently, as the second image shows.

12.3. Are static and final Fields Serialized?

Fields marked static are not serialized, because they belong to the class, whereas final instance fields are serialized like other fields. Java sets them on reading, even though no constructor runs.

12.4. Is Java Serialization Deprecated?

No. Serializable, ObjectOutputStream and ObjectInputStream are not deprecated in Java 25, and frameworks still use them, for example to copy HTTP sessions between servers. For new code, we prefer JSON or protobuf for data that leaves the JVM and keep Java serialization for short-lived data between trusted Java programs.

12.5. Can Inner Classes and Lambdas Be Serialized?

Yes, but we should avoid it for inner classes. An inner class (a non-static class inside another class) holds a hidden reference to the outer object, so serializing the inner object also writes the outer object. The byte format also depends on the compiler, so we use a static nested class or a top-level class instead.

A lambda is serializable only when its target type is serializable, for example when we cast it to Runnable & Serializable. Its byte format also depends on the compiler.

13. Conclusion

Java serialization turns an object and the objects it points to into bytes with ObjectOutputStream.writeObject(), and ObjectInputStream.readObject() turns the bytes back into a new object. A few habits keep serialization safe.

  • Declare serialVersionUID.
  • Mark caches and secrets transient.
  • Check values in readObject(), or use a record whose constructor checks them.
  • Add readResolve() to serializable singletons.

We never read bytes from an untrusted source. When we need Java serialization at all, we set an allow-list filter, and for data that other systems read, we use JSON or protobuf.

14. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. It’s important to understand that when an instance is serialized, the byte code for its class is not serialized. Serialization applies only to the data carried by the class. When serialized, the fully-qualified class name is included in the instance; the instance created upon deserialization is of the class with the same fully-qualified class name in the program that is reading the serialization stream.

    What does this mean?

    It means you have to be careful when refactoring! If you serialize data from one version of an application, then refactor the application to move one of the serialized classes from one package to another, deserialization will fail!

    It means you should avoid serialization of anonymous classes! Anonymous classes “have no name”, but when serializing an instance of one, Java will create a name using the file and the order of the class declaration in the file. This breaks deserialization if you remove an anonymous class declaration, or if you rearrange the code in a way that changes the lexical order of anonymous class declarations!

    • Static variables are not serialized when an instance of a class is serialized, because they belong to the class, not to the instance.

      The Class class itself is Serializable, and my guess is that if its static members are Serializable, they will be serialized along with the Class — so you might be able to use that to serialize static variables — but the deserialized Class won’t replace the local instance of the Class of the same name, so you’d have to manually replace the static variables of the local Class after deserialization, and at that point you might as well just write a Class to hold non-static copies of the static variables of interest and serialize that instead.

  2. Hi Lokesh,

    Nice work :) Every software developers dream to summarize all FAQ of JAVA at one place.

    I have been asked in an interview how to achieve Serialization if the object is Singleton.
    Any thought on this.

    Thanks a bunch.

    • Give the object a method “protected Object readResolve() throws ObjectStreamException”.

      This method is called after deserialization of an instance from the ObjectInputStream is complete, but before the result is assigned to any variable. The default implementation returns the deserialized instance, but you can override it to return any Object (including ones that will break your code, so don’t do those); in your case, to return the Singleton.

      For instance, if you have a large number of instances of a Serializable class that delegates some computation “SomeFunction.doSomeFunction(int x, int y)” to a Singleton that implements Serializable and SomeFunction, as shown below. The readResolve implementation ensures that all class instances that referred to _singleton when serialized end up referring to (the active instance of) _singleton after deserialization, rather than each carrying its own anonymous copy.

  3. Hi Lokesh,

    Can you please tell us how can we control the fields to be serialized with the help of readObject.
    I dont want to use transient .

  4. Hi lokes, I have a query regarding serialization. As we know that at the time of deserialization the class constructor does not run. As far as i know if we deserialize our object, its constructors doesn’t get called, but default constructor of its parent will be called. My question is that at the time of the deserializtion, how the jvm creates the object without calling the constructor ??
    I serialized a class’s object and then deserialized it and compared the references. Both references were not same. Please can you help :)

    • While serializing an object, the object’s type (class metadata) , field’s data types and field’s data values are converted into a series of bytes and written in form of bytes. This same information is used in reconstructing the object back during deserialization process.

      In fact, this is very interesting topic and I just thought to write a separate post on it. Wait till tomorrow. I will explain in detail.

      • Good article. I have come across a framework where the bean objects which get stored in session are to be serialized (a kind of mandate). Few modern day frameworks involve concepts of passivation of data.

      • in case the DTO object is changed as incompatible level later , what will happen to the old serialized session data (DTO object) in the server? is it get cleaned automatically? assume serialVersionUID is incremented to avoid mixing different versions as you mentioned in the related article.
        Congrats for your great texts!!!

  5. I am understanding what text you have provided in this guide….It helping me a lot. but i could not able to understand the code thoroughly. Can you give me a suggestion for that and how to be a good programmer?

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.