Serialization in java -NotSerializableException

Sunaina Goyal
9 min readFeb 18, 2021

--

Serialization is a mechanism of converting the state of an object into a byte stream. Deserialization is the reverse process where the byte stream is used to recreate the actual Java object in memory. This mechanism is used to persist the object.

The Technical Rundown

All Java errors implement the java.lang.Throwable interface, or are extended from another inherited class therein. The full exception hierarchy of this error is:

WHEN CAN IT OCCUR

1. When you serialize an inner class instance

java.io.NotSerializableException can occur when you serialize an inner class instance because:

serializing such an inner class instance will result in serialization of its associated outer class instance as well

Serialization of inner classes (i.e., nested classes that are not static member classes), including local and anonymous classes, is strongly discouraged

2. When attempting to serialize or deserialize an object that does not implement the java.io.Serializable interface

Below is the full code sample:

package io.airbrake;

import io.airbrake.utility.Logging;

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

public class Main {

public static void main(String[] args) {
// Create book and test serialization.
Book nameOfTheWind = new Book("The Name of the Wind", "Patrick Rothfuss", 662, new GregorianCalendar(2007, 2, 27).getTime());
testBookSerialization(nameOfTheWind);

// Create and test another book.
Book wiseMansFear = new Book("The Wise Man's Fear", "Patrick Rothfuss", 994, new GregorianCalendar(2011, 2, 1).getTime());
testBookSerialization(wiseMansFear);
}

/**
* Tests serialization of passed Book object.
*
* @param book Book to be tested.
*/
private static void testBookSerialization(Book book) {
// Determine serialization file name.
String fileName = String.format("%s.ser", book.toFilename());
// Output file info.
Logging.lineSeparator(String.format("SAVING TO FILE: %s", fileName), 75);
// Serialize Book to file.
serialize(book, fileName);
// Deserialize Book from file.
deserialize(fileName);
}

/**
* Serializes the passed object to the passed filePath.
*
* @param object Object that is being serialized.
* @param filePath File path where object should be stored.
*/
private static void serialize(Object object, String filePath) {
try {
FileOutputStream fileOutputStream = new FileOutputStream(filePath);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(fileOutputStream);
objectOutputStream.writeObject(object);
fileOutputStream.close();
Logging.log(String.format("SERIALIZED [%s]: %s", object.getClass().getName(), object));
} catch (NotSerializableException exception) {
// Output expected NotSerializeableExceptions.
Logging.log(exception);
} catch (IOException exception) {
// Output unexpected IOException.
Logging.log(exception, false);
}
}

/**
* Deserializes object found in passed filePath.
*
* @param filePath Path to file where serialized object is found.
* @param <T> Type of object that is expected.
*/
private static <T> void deserialize(String filePath) {
try {
FileInputStream fileInputStream = new FileInputStream(filePath);
ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream);
T object = (T) objectInputStream.readObject();
objectInputStream.close();
fileInputStream.close();
Logging.log(String.format("DESERIALIZED [%s]: %s", object.getClass().getName(), object));
} catch (NotSerializableException exception) {
// Output expected NotSerializeableExceptions.
Logging.log(exception);
} catch (IOException | ClassNotFoundException exception) {
// Output unexpected IOExceptions and ClassNotFoundExceptions.
Logging.log(exception, false);
}
}
}
// Book.java
package io.airbrake;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.annotation.*;
import io.airbrake.utility.Logging;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.text.DateFormat;
import java.util.Date;

/**
* Simple example class to store book instances.
*/
@JsonIgnoreProperties(ignoreUnknown = true)
public class Book implements Serializable
{
private String author;
private String title;
private Integer pageCount;
private Date publishedAt;
private static String publicationType = "Book";

private static final Integer maximumPageCount = 4000;

/**
* Ensure publication type is upper case.
*/
static {
publicationType = publicationType.toUpperCase();
}

/**
* Constructs an empty book.
*/
public Book() { }

/**
* Constructs a basic book.
*
* @param title Book title.
* @param author Book author.
*/
public Book(String title, String author) {
setAuthor(author);
setTitle(title);
}

/**
* Constructs a basic book, with page count.
*
* @param title Book title.
* @param author Book author.
* @param pageCount Book page count.
*/
public Book(String title, String author, Integer pageCount) {
setAuthor(author);
setPageCount(pageCount);
setTitle(title);
}

/**
* Constructs a basic book, with page count.
*
* @param title Book title.
* @param author Book author.
* @param pageCount Book page count.
*/
public Book(String title, String author, Integer pageCount, Date publishedAt) {
setAuthor(author);
setPageCount(pageCount);
setTitle(title);
setPublishedAt(publishedAt);
}

/**
* Constructs a basic book, with page count.
*
* @param title Book title.
* @param author Book author.
* @param pageCount Book page count.
*/
public Book(String title, String author, Integer pageCount, Date publishedAt, String publicationType) {
setAuthor(author);
setPageCount(pageCount);
setTitle(title);
setPublishedAt(publishedAt);
setPublicationType(publicationType);
}

/**
* Get author of book.
*
* @return Author name.
*/
public String getAuthor() {
return author;
}

/**
* Get page count of book.
*
* @return Page count.
*/
public Integer getPageCount() {
return pageCount;
}

/**
* Get publication type of book.
*
* @return Publication type.
*/
public String getPublicationType() { return publicationType; }

/**
* Get published date of book.
*
* @return Published date.
*/
public Date getPublishedAt() { return publishedAt; }

/**
* Get a formatted tagline with author, title, page count, and publication date.
*
* @return Formatted tagline.
*/
public String getTagline() {
return String.format("'%s' by %s is %d pages, published %s.",
getTitle(),
getAuthor(),
getPageCount(),
DateFormat.getDateInstance().format(getPublishedAt()));
}

/**
* Get title of book.
*
* @return Title.
*/
public String getTitle() {
return title;
}

/**
* Publish current book.
* If book already published, throws IllegalStateException.
*/
public void publish() throws IllegalStateException {
Date publishedAt = getPublishedAt();
if (publishedAt == null) {
setPublishedAt(new Date());
Logging.log(String.format("Published '%s' by %s.", getTitle(), getAuthor()));
} else {
throw new IllegalStateException(
String.format("Cannot publish '%s' by %s (already published on %s).",
getTitle(),
getAuthor(),
publishedAt));
}
}

/**
* Set author of book.
*
* @param author Author name.
*/
public void setAuthor(String author) {
this.author = author;
}

/**
* Set page count of book.
*
* @param pageCount Page count.
*/
public void setPageCount(Integer pageCount) throws IllegalArgumentException {
if (pageCount > maximumPageCount) {
throw new IllegalArgumentException(String.format("Page count value [%d] exceeds maximum limit [%d].", pageCount, maximumPageCount));
}
this.pageCount = pageCount;
}

/**
* Set publication type of book.
*
* @param type Publication type.
*/
public void setPublicationType(String type) { this.publicationType = type; }

/**
* Set published date of book.
*
* @param publishedAt Page count.
*/
public void setPublishedAt(Date publishedAt) {
this.publishedAt = publishedAt;
}

/**
* Set title of book.
*
* @param title Title.
*/
public void setTitle(String title) {
this.title = title;
}

/**
* Get the filename formatted version of this Book.
*
* @return Filename.
*/
String toFilename() {
try {
return java.net.URLEncoder.encode(String.format("%s-%s", getTitle(), getAuthor()).toLowerCase(), "UTF-8");
} catch (UnsupportedEncodingException exception) {
Logging.log(exception);
}
return null;
}

/**
* Output to JSON string.
*
* @return
* @throws JsonProcessingException
*/
public String toJsonString() throws JsonProcessingException {
return new ObjectMapper().writeValueAsString(this);
}

/**
* Get string representation of Book.
*
* @return String representation.
*/
public String toString() {
return getTagline();
}

/**
* Throw an Exception.
*/
public void throwException(String message) throws Exception {
throw new Exception(message);
}
}

To test the serialization and deserialization process, we have the testBookSerialization(Book book) wrapper method:

/**
* Tests serialization of passed Book object.
*
* @param book Book to be tested.
*/
private static void testBookSerialization(Book book) {
// Determine serialization file name.
String fileName = String.format("%s.ser", book.toFilename());
// Output file info.
Logging.lineSeparator(String.format("SAVING TO FILE: %s", fileName), 75);
// Serialize Book to file.
serialize(book, fileName);
// Deserialize Book from file.
deserialize(fileName);
}

As you can see, all we’re doing here is creating a local file name (using the Book.toFilename()), then attempting to first serialize and then deserialize the passed Book book object.

To see if everything works as expected our Main.main(...) method creates two different Book instances and performs our serialization tests:

public static void main(String[] args) {
// Create book and test serialization.
Book nameOfTheWind = new Book("The Name of the Wind", "Patrick Rothfuss", 662, new GregorianCalendar(2007, 2, 27).getTime());
testBookSerialization(nameOfTheWind);
// Create and test another book.
Book wiseMansFear = new Book("The Wise Man's Fear", "Patrick Rothfuss", 994, new GregorianCalendar(2011, 2, 1).getTime());
testBookSerialization(wiseMansFear);
}

Executing the Main.main() method produces the following output:

-------- SAVING TO FILE: the+name+of+the+wind-patrick+rothfuss.ser --------
SERIALIZED [io.airbrake.Book]: 'The Name of the Wind' by Patrick Rothfuss is 662 pages, published Mar 27, 2007.
DESERIALIZED [io.airbrake.Book]: 'The Name of the Wind' by Patrick Rothfuss is 662 pages, published Mar 27, 2007.
------- SAVING TO FILE: the+wise+man%27s+fear-patrick+rothfuss.ser --------
SERIALIZED [io.airbrake.Book]: 'The Wise Man's Fear' by Patrick Rothfuss is 994 pages, published Mar 1, 2011.
DESERIALIZED [io.airbrake.Book]: 'The Wise Man's Fear' by Patrick Rothfuss is 994 pages, published Mar 1, 2011.

Everything seems to be working just as expected. Our encoded filenames are created, and our Book instances are successfully being serialized and saved to the file, then deserialized. To confirm, if we open up the the+name+of+the+wind-patrick+rothfuss.ser local file the contents look like this:

�� sr io.airbrake.Bookm���smh4 L authort Ljava/lang/String;L  pageCountt Ljava/lang/Integer;L publishedAtt Ljava/util/Date;L titleq ~ xpt Patrick Rothfusssr java.lang.Integer⠤���8 I valuexr java.lang.Number������  xp  �sr java.util.Datehj�KYt  xpw  �1��xt The Name of the Wind

Looks like a mess to us since it’s not stored in a human-readable format, but this further confirms that our serialization is working properly.

However, let’s pretend we did everything as before, expect for one minor mistake: We forgot to add implements Serializable to our Book class definition:

public class Book
{
// ...
}

With all other testing code exactly as it was before, executing our Main.main() method again now produces the following output:

-------- SAVING TO FILE: the+name+of+the+wind-patrick+rothfuss.ser --------
[EXPECTED] java.io.NotSerializableException: io.airbrake.Book
[UNEXPECTED] java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: io.airbrake.Book
------- SAVING TO FILE: the+wise+man%27s+fear-patrick+rothfuss.ser --------
[EXPECTED] java.io.NotSerializableException: io.airbrake.Book
[UNEXPECTED] java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: io.airbrake.Book

Suddenly we experience our first NotSerializableExceptions, which indicate that the io.airbrake.Book class is not serializable. In this case, we know it’s because we didn’t implement the Serializable interface. However, it’s worth noting that serialization can only work if the entire series of related objects are serializable. This means that, if one of the fields of our Book class was private Publisher publisher, where Publisher was a unique class of its own, we could only serialize a Book instance if both Book and Publisher classes implemented the Serializable interface. Alternatively, we could also serialize a Book instance even if Publisher did not implement Serializable, but only if the private Publisher publisher field was annotated as a transient field. The transient annotation indicates that the field should not be serialized.

3. If the superclass is serializable but we don’t want the subclass to be serialized.

It can be achieved by implementing the writeObject() and readObject() methods in the subclass and throwing NotSerializableException from these methods.

// Java program to demonstrate

// how to prevent

// subclass from serialization

import java.io.FileInputStream;

import java.io.FileOutputStream;

import java.io.IOException;

import java.io.NotSerializableException;

import java.io.ObjectInputStream;

import java.io.ObjectOutputStream;

import java.io.Serializable;

// superclass A

// implementing Serializable interface

class A implements Serializable

{

int i;

// parameterized constructor

public A(int i)

{

this.i = i;

}

}

// subclass B

// B class doesn't implement Serializable

// interface.

class B extends A

{

int j;

// parameterized constructor

public B(int i,int j)

{

super(i);

this.j = j;

}

// By implementing writeObject method,

// we can prevent

// subclass from serialization

private void writeObject(ObjectOutputStream out) throws IOException

{

throw new NotSerializableException();

}

// By implementing readObject method,

// we can prevent

// subclass from de-serialization

private void readObject(ObjectInputStream in) throws IOException

{

throw new NotSerializableException();

}

}

// Driver class

public class Test

{

public static void main(String[] args)

throws Exception

{

B b1 = new B(10, 20);

System.out.println("i = " + b1.i);

System.out.println("j = " + b1.j);

// Serializing B's(subclass) object

//Saving of object in a file

FileOutputStream fos = new FileOutputStream("abc.ser");

ObjectOutputStream oos = new ObjectOutputStream(fos);

// Method for serialization of B's class object

oos.writeObject(b1);

// closing streams

oos.close();

fos.close();

System.out.println("Object has been serialized");

// De-Serializing B's(subclass) object

// Reading the object from a file

FileInputStream fis = new FileInputStream("abc.ser");

ObjectInputStream ois = new ObjectInputStream(fis);

// Method for de-serialization of B's class object

B b2 = (B)ois.readObject();

// closing streams

ois.close();

fis.close();

System.out.println("Object has been deserialized");

System.out.println("i = " + b2.i);

System.out.println("j = " + b2.j);

}

}

Output:

i = 10
j = 20
Exception in thread "main" java.io.NotSerializableException
at B.writeObject(Test.java:44)

4. When superclass implements marker interface

The marker interface pattern is a design pattern in computer science, used with languages that provide run-time type information about objects. It provides a means to associate metadata with a class where the language does not have explicit support for such metadata. In java, it is used as interfaces with no method specified.

A major problem with marker interfaces is that an interface defines a contract for implementing classes, and that contract is inherited by all subclasses. This means that you cannot “un-implement” a marker. In the example given, if you create a subclass that you do not want to serialize (perhaps because it depends on transient state), you must resort to explicitly throwing NotSerializableException (as shown above in point 3).

Please refer to my article on Serialization in java for more details. You may write to me in the comments, if you wish to contribute.

--

--

Sunaina Goyal
Sunaina Goyal

Written by Sunaina Goyal

Developer, melomaniac, philospher, tea lover

No responses yet