Skip to main content

Command Palette

Search for a command to run...

Serialization and Deserialization in Java

Object to Byte Stream and Byte Stream to Object

Updated
3 min readView as Markdown
Serialization and Deserialization in Java
A

Product-oriented Software Engineer with a solid understanding of web programming fundamentals and software development methodologies such as agile and scrum.

Serialization is the process of converting an object into a byte stream, which can be saved to a file, sent over a network, or stored in a database. This byte stream can later be deserialized to recreate the original object. Java provides built-in support for both serialization and deserialization through the java.io package.

Why Use Serialization and Deserialization?

Serialization and deserialization are used in various scenarios, such as:

  • Storing object states in files or databases.

  • Sending objects over a network between applications.

  • Implementing caching mechanisms.

  • Persisting session states in web applications.

  • Cloning objects.

  • Facilitating distributed computing.

How Does It Work?

Java provides the Serializable interface to enable serialization. A class must implement this interface to allow its objects to be serialized and later deserialized.

Implementing Serialization and Deserialization

To serialize and deserialize an object in Java:

  1. The class must implement the java.io.Serializable interface.

  2. Use ObjectOutputStream to write the object to a file or stream.

  3. Use ObjectInputStream to read the object back and reconstruct it.

Example:

import java.io.*;

class Person implements Serializable {
    private static final long serialVersionUID = 1L;
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

public class SerializationDemo {
    public static void main(String[] args) {
        Person person = new Person("Alice", 25);

        // Serialize the object
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
            out.writeObject(person);
            System.out.println("Serialization successful");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // Deserialize the object
        try (ObjectInputStream in = new ObjectInputStream(new FileInputStream("person.ser"))) {
            Person deserializedPerson = (Person) in.readObject();
            System.out.println("Deserialization successful: " + deserializedPerson.name + ", " + deserializedPerson.age);
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

Transient Keyword

If a field should not be serialized, mark it as transient. The transient keyword prevents sensitive or unnecessary data from being stored during serialization, such as passwords or temporary variables.

Example:

class Account implements Serializable {
    private static final long serialVersionUID = 1L;
    String username;
    transient String password; // This field will not be serialized

    public Account(String username, String password) {
        this.username = username;
        this.password = password;
    }
}

When an object of Account is serialized, the password field will not be included, ensuring that sensitive information is not stored.

serialVersionUID

The serialVersionUID is used to ensure compatibility between serialized objects across different versions of a class. If not declared, Java generates one automatically, but it's recommended to define it explicitly to avoid InvalidClassException when class modifications occur.

private static final long serialVersionUID = 1L;

Externalizable Interface

For more control over serialization, a class can implement the Externalizable interface, overriding writeExternal() and readExternal() methods. This provides flexibility but requires manual handling of all fields.

class CustomData implements Externalizable {
    String data;

    public CustomData(String data) {
        this.data = data;
    }

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

    @Override
    public void readExternal(ObjectInput in) throws IOException {
        data = in.readUTF();
    }
}

Deserialization Considerations

While deserialization is a powerful feature, it poses some security and integrity risks:

  • Security Issues: Deserializing untrusted data can lead to exploits, such as remote code execution vulnerabilities.

  • Data Integrity: If the class structure has changed significantly since serialization, deserialization may fail.

  • Performance Costs: Large objects can be expensive to serialize and deserialize, impacting application performance.

Best Practices:

  • Always define serialVersionUID explicitly.

  • Use transient for sensitive fields.

  • Prefer Externalizable for more control.

  • Validate input during deserialization to prevent attacks.

  • Implement deserialization safeguards, such as checking object types before casting.

Conclusion

Serialization and deserialization in Java enable object persistence and data exchange. However, they should be used cautiously, keeping security and performance concerns in mind. Understanding Serializable, transient, serialVersionUID, Externalizable, and deserialization risks ensures effective and secure serialization practices.

Learning Java

Part 16 of 26

In this series, I will break down the task of learning java into smaller actionable steps and work on them topic by topic while trying to provide observability into my learning process.

Up next

Interfaces and Their Types in Java

Understanding Interfaces and Their Types in Java