What Is Serialization PDF

Download as pdf or txt
Download as pdf or txt
You are on page 1of 1

Sign in Get started

What is Serialization? Everything You


Need to Know About Java Serialization
Explained With Example
Naresh Joshi Follow
Aug 30, 2019 · 10 min read

In a previous article, we looked at 5 different ways to create objects in


java, I have explained how deserialising a serialised object creates a new
object and in this blog, I am going to discuss Serialization and
Deserialization in details.

We will use below Employee class object as an example for the


explanation

// If we use Serializable interface, static and transient variables


do not get serialize
class Employee implements Serializable {

// This serialVersionUID field is necessary for Serializable as well


as Externalizable to provide version control,
// Compiler will provide this field if we do not provide it which
might change if we modify the class structure of our class, and we
will get InvalidClassException,
// If we provide value to this field and do not change it,
serialization-deserialization will not fail if we change our class
structure.
private static final long serialVersionUID = 2L;

private final String firstName; // Serialization process do not


invoke the constructor but it can assign values to final fields
private transient String middleName; // transient variables will
not be serialized, serialised object holds null
private String lastName;
private int age;
private static String department; // static variables will not be
serialized, serialised object holds null

public Employee(String firstName, String middleName, String lastName,


int age, String department) {
this.firstName = firstName;
this.middleName = middleName;
this.lastName = lastName;
this.age = age;
Employee.department = department;

validateAge();
}

private void validateAge() {


System.out.println("Validating age.");

if (age < 18 || age > 70) {


throw new IllegalArgumentException("Not a valid age to
create an employee");
}
}

@Override
public String toString() {
return String.format("Employee {firstName='%s',
middleName='%s', lastName='%s', age='%s', department='%s'}",
firstName, middleName, lastName, age, department);
}

// Custom serialization logic,


// This will allow us to have additional serialization logic on
top of the default one e.g. encrypting object before serialization
private void writeObject(ObjectOutputStream oos) throws
IOException {
System.out.println("Custom serialization logic invoked.");
oos.defaultWriteObject(); // Calling the default
serialization logic
}

// Custom deserialization logic


// This will allow us to have additional deserialization logic on
top of the default one e.g. decrypting object after deserialization
private void readObject(ObjectInputStream ois) throws
IOException, ClassNotFoundException {
System.out.println("Custom deserialization logic invoked.");

ois.defaultReadObject(); // Calling the default deserialization logic

// Age validation is just an example but there might some scenario


where we might need to write some custom deserialization logic
validateAge();
}
}

What are Serialization and Deserialization


In Java, we create several objects which live and die accordingly and
every object will certainly die when the JVM dies but sometimes we might
want to reuse an object between several JVMs or we might want to
transfer an object to another machine over the network.

Well, serialization allows us to convert the state of an object into a byte


stream, which then can be saved into a file on the local disk or sent over
the network to any other machine. And deserialization allows us to
reverse the process, which means reconverting the serialized byte stream
to an object again.

In simple words, object serialization is the process of saving an object’s


state to a sequence of bytes and deserialization is the process of
reconstructing an object from those bytes. Generally, the complete
process is called serialization but I think it is better to classify both as
separate for more clarity.

The serialization process is platform independent, an object serialized


on one platform can be deserialized on a different platform.

To serialize and deserialize our object to a file we need to call


ObjectOutputStream.writeObject() and ObjectInputStream.readObject() as
done in the following code:

public class SerializationExample {


public static void main(String[] args) throws IOException,
ClassNotFoundException {
Employee empObj = new Employee("Shanti", "Prasad", "Sharma",
25, "IT");
System.out.println("Object before serialization => " +
empObj.toString());

// Serialization
serialize(empObj);

// Deserialization
Employee deserialisedEmpObj = deserialize();
System.out.println("Object after deserialization => " +
deserialisedEmpObj.toString());
}

// Serialization code
static void serialize(Employee empObj) throws IOException {
try (FileOutputStream fos = new FileOutputStream("data.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos))
{
oos.writeObject(empObj);
}
}

// Deserialization code
static Employee deserialize() throws IOException,
ClassNotFoundException {
try (FileInputStream fis = new FileInputStream("data.obj");
ObjectInputStream ois = new ObjectInputStream(fis))
{
return (Employee) ois.readObject();
}
}
}

Only classes which implement Serializable can be


serialized
Similar to the Cloneable interface for Java cloning in serialization, we
have one marker interface Serializable which works like a flag for the
JVM. Any class which implements Serializable interface directly or
through its parent can be serialised and classes which do not implement
Serializable can not be serialized.

Java’s default serialization process is fully recursive, so whenever we try


to serialize one object, the serialization process try to serialize all the
fields (primitive and reference) with our class (except static and
transient fields).

When a class implements the Serializable interface, all its sub-classes


are serializable as well. But when an object has a reference to another
object, these objects must implement the Serializable interface
separately. If our class is having even a single reference to a non
Serializable class then JVM will throw NotSerializableException .

Why Serializable is not implemented by Object?


Now a question arises if Serialization is very basic functionality and any
class which do not implement Serializable can not be serialised, then
why Serializable is not implemented by the Object itself?, By this way, all
our objects could be serialized by default.

The Object class does not implement Serializable interface because we


may not want to serialize all the objects e.g. serialising a thread does not
make any sense because thread running in my JVM would be using my
system's memory, persisting it and trying to run it in your JVM would
make no sense.

The transient and static fields do not get serialized


If we want to serialize one object but do not want to serialize some
specific fields then we can mark those fields as transient.

All the static fields belong to the class instead of the object, and the
serialization process serialises the object so static fields can not be
serialized.

1. Serialization does not care about access modifiers of the field such as
private . All non-transient and non-static fields are considered part of
an object's persistent state and are eligible for serialisation.

2. We can assign values to final fields in constructors only and


serialization process does not invoke any constructor but still, it can
assign values to final fields.

What is serialVersionUID and Why should we declare


it?
Suppose we have a class and we have serialized its object to a file on the
disk, and due to some new requirements, we added/removed one field
from our class. Now if we try to deserialize the already serialized object
we will get InvalidClassException , why?

We get it because by default JVM associates a version number to each


serializable class to control the class versioning. It is used to verify that
the serialized and deserialized objects have the same attributes and thus
are compatible with deserialization. The version number is maintained in
a field called serialVersionUID . If a serializable class doesn't declare a
serialVersionUID JVM will generate one automatically at run-time.

If we change our class structure e.g. remove/add fields that version


number also changes and according to JVM our class is not compatible
with the class version of the serialized object. That’s why we get the
exception but if you really think about it, why should it be thrown just
because I added a field? Couldn’t the field just be set to its default value
and then written out next time?

Yes, it can be done by providing the serialVersionUID field manually and


ensure it is always the same. It is highly recommended that each
serializable class declares its serialVersionUID as the generated one is
compiler dependent and thus may result in unexpected
InvalidClassExceptions.

You can use a utility that comes with the JDK distribution called
serialver to see what that code would be by default (it is just the hash
code of the object by default).

Customizing Serialization and Deserialization with


writeObject and readObject methods
JVM has full control for serializing the object in the default serialization
process but there are lots of downside of the using default serialization
process, some of which are:

1. It can not handle serialisation of fields which are not serializable.

2. Deserialization process does not invoke constructors while creating


the object so it can not call the initialization logic provided by the
constructor.

But we can override this the default serialization behaviour inside our
Java class and provide some additional logic to enhance the normal
process. This can be done by providing two methods writeObject and
readObject inside the class that we want to serialize:

// Custom serialization logic will allow us to have additional


serialization logic on top of the default one e.g. encrypting object
before serialization
private void writeObject(ObjectOutputStream oos) throws IOException {
// Any Custom logic
oos.defaultWriteObject(); // Calling the default serialization logic
// Any Custom logic
}

// Custom deserialization logic will allow us to have additional


deserialization logic on top of the default one e.g. decrypting
object after deserialization
private void readObject(ObjectInputStream ois) throws IOException,
ClassNotFoundException {
// Any Custom logic
ois.defaultReadObject(); // Calling the default deserialization
logic
// Any Custom logic
}

Declaring both methods as private is necessary (public methods will not


work) so rather than JVM nothing else can see them. This also proves
that neither method is not inherited nor overridden or overloaded. JVM
automatically checks these methods and call them during the
serialization-deserialization process. JVM can call these private methods
but other objects can not thus, the integrity of the class is maintained and
the serialization protocol can continue to work as normal.

Even though those specialized private methods are provided, the object
serialization works the same way by calling
ObjectOutputStream.writeObject() or ObjectInputStream.readObject() .

The call to ObjectOutputStream.writeObject() or


ObjectInputStream.readObject() kicks off the serialization protocol. First,
the object is checked to ensure it implements Serializable and then it is
checked to see whether either of those private methods is provided. If
they are provided, the stream class is passed as the parameter to these
methods, giving the code control over its usage.

We can call ObjectOutputStream.defaultWriteObject() and


ObjectInputStream.defaultReadObject() from these methods to gain
default serialization logic. Those calls do what they sound like -- they
perform the default writing and reading of the serialized object, which is
important because we are not replacing the normal process, we are only
adding to it.

Those private methods can be used for any customization you want to
make in the serialization process, e.g. encryption can be added to the
output and decryption to the input (note that the bytes are written and
read in cleartext with no obfuscation at all). They could be used to add
extra data to the stream, perhaps a company versioning code, the
possibilities are truly limitless.

Apart from writeObject and readObject methods, we can also leverage


other serialization magic methods to get some additional functionality.

Stopping Serialization and Deserialization


Suppose we have a class which got the serialization capability from its
parent, which means our class extends from another class which
implements Serializable .

It means anybody can serialize and deserialize the object of our class. But
what if we do not want our class to be serialized or deserialized e.g. our
class is a singleton and we want to prevent any new object creation,
remember the deserialization process creates a new object.

To stop the serialization for our class, we can once again use the above
private methods to just throw the NotSerializableException . Any attempt
to serialize or deserialise our object will now always result in the
exception being thrown. And since those methods are declared as
private, nobody can override your methods and change them.

private void writeObject(ObjectOutputStream oos) throws IOException {


throw new NotSerializableException("Serialization is not supported
on this object!");
}

private void readObject(ObjectInputStream ois) throws IOException,


ClassNotFoundException {
throw new NotSerializableException("Serialization is not supported
on this object!");
}

However, this is a violation of the Liskov Substitution Principle. And


writeReplace and readResolve methods can be used to achieve singleton
like behaviours. These methods are used to allow an object to provide an
alternative representation for itself within an ObjectStream. In simple
words, readResolve can be used to change the data that is deserialized
through the readObject method and writeReplace can be used to change
the data that is serialized through writeObject.

Java serialization can also be used to deep clone an object. Java cloning
is the most debatable topic in Java community and it surely does have its
drawbacks but it is still the most popular and easy way of creating a copy
of an object until that object is full filling mandatory conditions of Java
cloning. I have covered cloning in details in a 3 article long Java Cloning
Series which includes articles like Java Cloning And Types Of Cloning
(Shallow And Deep) In Details With Example, Java Cloning — Copy
Constructor Versus Cloning, Java Cloning — Even Copy Constructors Are
Not Sufficient, go ahead and read them if you want to know more about
cloning.

Conclusion
1. Serialization is the process of saving an object’s state to a sequence
of bytes which then can be stored on a file or sent over the network
and deserialization is the process of reconstructing an object from
those bytes.

2. Only subclasses of the Serializable interface can be serialized.

3. If our class does not implement Serializable interface or if it is having


a reference to a non Serializable class then JVM will throw
NotSerializableException .

4. All transient and static fields do not get serialized.

5. The serialVersionUID is used to verify that the serialized and


deserialized objects have the same attributes and thus are compatible
with deserialization.

6. We should create a serialVersionUID field in our class so if we change


our class structure (adding/removing fields) JVM will not through
InvalidClassException . If we do not provide it JVM provides one which
might change when our class structure changes.

7. We can override the default serialization behaviour inside our Java


class by providing the implementation of writeObject and readObject

methods.

8. And we can call ObjectOutputStream.defaultWriteObject() and


ObjectInputStream.defaultReadObject from writeObject and
readObject methods to get the default serialization and
deserialization logic.

9. We can throw NotSerializableException exception from writeObject

and readObject , if we do not want our class to be serialized or


deserialized.

Java Serialization process can be further customized and enhanced using


the Externalizable interface which I have explained in How to Customize
Serialization In Java By Using Externalizable Interface.

I have also written a series of articles explaining item numbers 74 to 78


of Effective Java, which further discusses how Java serialization process
can be enhanced, please go ahead and read them if you like.

You can find the complete source code for this article on this Github
Repository and please feel free to provide your valuable feedback.

. . .

Originally published at https://www.programmingmitra.com.

Java Serialization

54 claps

WRIT T EN BY

Naresh Joshi Follow

I am a Java Programmer, Blogger working on Java, Spring,


Hibernate. I share Java tips on
https://www.programmingmitra.com

ProgrammingMitra.com Follow

A friend to Java Programming Language and its related


Frameworks

Write the rst response

More From Medium

Injecting and Mocking Measuring Your Heart Add Home Screen Quick Powering the Internet
static frameworks in Rate Using Your Phone’s Actions in Swift and iOS with Base64
Swift Camera and Flutter 13 Vickie Li in T he Startup
Cyril Le Pottier in Afonso Raposo in Better Domenico Nicoli in Better
Tech@OpenClassrooms Programming Programming

Manipulating File Paths CSS for Beginners What’s in the Google Create a REST full API
with Python Cem Eygi in Better PageSpeed Score? using Python and Flask
John Au-Yeung in Python In Programming Csaba Pal in Expedia Group (Part 1)
Plain English Technology Kasun Dissanayake

Discover Medium Make Medium yours Become a member


Welcome to a place where words matter. On Medium, smart Follow all the topics you care about, and we’ll deliver the Get unlimited access to the best stories on Medium — and
voices and original ideas take center stage - with no ads in best stories for you to your homepage and inbox. Explore support writers while you’re at it. Just $5/month. Upgrade
sight. Watch

About Help Legal

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy