This Java tutorial helps you understand object serialization in Java, why you need it, how it works, when to use it, related concepts (serialVersionUID and transient), and other necessary information about serialization and deserialization.

The serialization examples in this tutorial are kept simple in order to help you get the important points.

 

1. Why Java Serialization?

Serialization is a mechanism for storing an object’s states into a persistent storage like disk files, databases, or sending object’s states over the network. The process of retrieval and construction of objects from disk files, databases and network is called de-serialization.

Here are some examples of using serialization:

- Storing data in an object-oriented way to files on disk, e.g. storing a list of Student objects.

- Saving program’s states on disk, e.g. saving state of a game.

- Sending data over the network in form objects, e.g. sending messages as objects in chat application.

 

2. How does Serialization Work in Java?

An object is eligible for serialization if and only if its class implements the java.io.Serializable interface. Serializable is a marker interface (contains no methods) that tell the Java Virtual Machine (JVM) that the objects of this class is ready for being written to and read from a persistent storage or over the network.

By default, the JVM takes care of the process of writing and reading serializable objects. The serialization/deserialization functionalities are exposed via the following two methods of the object stream classes:

  • ObjectOutputStream.writeObject(Object): writes a serializable object to the output stream. This method throws NotSerializableException if some object to be serialized does not implement the Serializable interface.
  • ObjectInputStream.readObject(): reads, constructs and returns an object from the input stream. This method throws ClassNotFoundException if class of a serialized object cannot be found.

Both methods throw InvalidClassException if something is wrong with a class used by serialization, and throw IOException if an I/O error occurs. Both NotSerializableException and InvalidClassException are sub classes of IOException.

Let’s see a quick example. The following code serializes a String object to a file named ‘data.ser’. String objects are serializable because the String class implements the Serializable interface:

String filePath = "data.ser";
String message = "Java Serialization is Cool";

try (
	FileOutputStream fos = new FileOutputStream(filePath);
	ObjectOutputStream outputStream = new ObjectOutputStream(fos);
) {

	outputStream.writeObject(message);

} catch (IOException ex) {
	System.err.println(ex);
}

And the following code deserializes a String object in the file ‘data.ser’:

String filePath = "data.ser";

try (
	FileInputStream fis = new FileInputStream(filePath);
	ObjectInputStream inputStream = new ObjectInputStream(fis);
) {

	String message = (String) inputStream.readObject();

	System.out.println("Message: " + message);

} catch (ClassNotFoundException ex) {
	System.err.println("Class not found: " + ex);
} catch (IOException ex) {
	System.err.println("IO error: " + ex);
}

Note that the readObject() return an object of type Object so you need to cast it to the serializable class, String class in this case.

Let’s see a more complex example that involves in using a custom class.

Given the following Student class:

import java.io.*;
import java.util.*;

/**
 * Student.java
 * @author www.codejava.net
 */
public class Student extends Person implements Serializable {
	public static final long serialVersionUID = 1234L;

	private long studentId;
	private String name;
	private transient int age;

	public Student(long studentId, String name, int age) {
		super();
		this.studentId = studentId;
		this.name = name;
		this.age = age;

		System.out.println("Constructor");
	}

	public String toString() {
		return String.format("%d - %s - %d", studentId, name, age);
	}
}

There are 2 new things you see in this class:

- The constant of type long serialVersionUID.

- The member variable age is marked as transient.

Let me explain about them right now.

 

3. What is the serialVersionUID constant?

serialVersionUID is a constant that uniquely identifies a version of a serializable class. The JVM checks this constant during the deserialization process when an object is being constructed from an input stream. If the object being read has a serialVersionUID different than the one specified in the class, the JVM throws an InvalidClassException. This is to make sure that the object being constructed is compatible with its class in having the same serialVersionUID.

Note that the serialVersionUID is optional. That means the Java compiler will generate one if you don’t explicitly declare it.

So why should you declare a serialVersionUID explicitly?

Here’s the reason: The auto-generated serialVersionUID is calculated based on elements of the class: member variables, methods, constructors, etc. If one of these elements get change, the serialVersionUID will be changed as well. Imagine this situation:

- You wrote a program that stores some objects of the Student class to a file. The Student class doesn’t have a serialVersionUID explicitly declared.

- Some times later you update the Student class (e.g. adding a new private method), and now the auto-generated serialVersionUID gets changed as well.

- Your program fails to deserialize the Student objects written previously because there serialVersionUID are different. The JVM throws an InvalidClassException.

That’s why it’s recommended to add a serialVersionUID explicitly for a serializable class.

 

4. What is a transient variable?

In the Student class above, you see the member variable age is marked as transient, right? The JVM skips transient variables during the serialization process. That means the value of the age variable is not stored when the object is being serialized.

So if a member variable needs not to be serialized, you can mark it as transient.

The following code serializes a Student object to a file called ‘students.ser’:

String filePath = "students.ser";
Student student = new Student(123, "John", 22);

try (
	FileOutputStream fos = new FileOutputStream(filePath);
	ObjectOutputStream outputStream = new ObjectOutputStream(fos);
) {

	outputStream.writeObject(student);

} catch (IOException ex) {
	System.err.println(ex);
}

Notice that the variable age has the value of 22 before the object is serialized.

And the following code deserializes the Student object from the file:

String filePath = "students.ser";

try (
	FileInputStream fis = new FileInputStream(filePath);
	ObjectInputStream inputStream = new ObjectInputStream(fis);
) {

	Student student = (Student) inputStream.readObject();

	System.out.println(student);

} catch (ClassNotFoundException ex) {
	System.err.println("Class not found: " + ex);
} catch (IOException ex) {
	System.err.println("IO error: " + ex);
}

This code would print the following output:

123 - John - 0

You see, the value of the age variable is 0, which means it is not serialized.

 

5. More about Java Serialization

There’s some important information with regard to serialization you should know:

  • When an object is serialized, all other objects it refers to, are serialized as well, and so on, until the complete objects tree is serialized.
  • If a super class implements Serializable, then its sub classes do automatically.
  • When an instance of a serializable class is deserialized, the constructor doesn’t run.
  • If a super class doesn’t implement Serializable, then when a subclass object is deserialized, the super class constructor will run.
  • Static variables are not serialized because they are not part of the object itself.
  • If you serialize a collection or an array, every element must be serializable. A single non-serializable element will cause the serialization to fail (NotSerializableException).
  • Serializable classes in JDK include primitive wrappers (Integer, Long, Double, etc), String, Date, collection classes… For other classes, consult relevant Javadoc to know if they are serializable.

 

Related Tutorials:

 

Other Java File IO Tutorials:


About the Author:

is certified Java programmer (SCJP and SCWCD). He began programming with Java back in the days of Java 1.4 and has been passionate about it ever since. You can connect with him on Facebook and watch his Java videos on YouTube.



Add comment

   


Comments 

#3Trang Nguyen2021-05-10 19:52
The article is very clear. Thank you anh!
Quote
#2Chinedu2020-09-17 09:58
If transient fields aren't serialized why,is it important declaring them since they'll be left in their default values?
Quote
#1Michael Nzeukoue2018-11-16 15:59
The explaination is kept clear and simple.
Thanks
Quote