Java ObjectOutputStream Class


In Java, the ObjectOutputStream class, part of the java.io package, plays a critical role in serialization. It enables the process of writing (serializing) objects into an output stream, making it possible to store or transmit the objects in a format that can be restored (deserialized) later. The ObjectOutputStream is the counterpart to ObjectInputStream, which is used for deserializing (reading back) objects from streams.

Serialization in Java allows objects to be converted into a byte stream, which can then be written to a file or sent over a network. This is essential for object persistence or for sending complex data structures in distributed systems. ObjectOutputStream is used to serialize objects, while ObjectInputStream is used for deserialization.

In this post, we will delve into the ObjectOutputStream class, explore its key methods, and show you practical examples of how to use it for writing objects to files and streams.


What is the ObjectOutputStream Class?

The ObjectOutputStream class is a subclass of OutputStream, and its primary function is to serialize objects to an output stream. Once serialized, the object can be written to a variety of destinations, such as a file, database, or network socket.

Serialization involves converting an object into a byte stream, which can then be saved or transmitted. The object must implement the Serializable interface for this process to work. Non-serializable objects will cause an error when you attempt to serialize them.


Key Methods of the ObjectOutputStream Class

The ObjectOutputStream class provides several essential methods for serializing objects. Here are the most important ones:

  1. void writeObject(Object obj):
    Serializes the specified object and writes it to the output stream. If the object is not serializable, a java.io.NotSerializableException will be thrown.

    objectOutputStream.writeObject(someObject);
    
  2. void write(byte[] b):
    Writes an array of bytes to the stream, similar to how bytes are written in other streams.

  3. void flush():
    Flushes the stream, ensuring that any data buffered by the stream is written to the destination.

  4. void close():
    Closes the output stream and releases any system resources associated with it.

  5. void writeObjectOverride(Object obj):
    This method can be used for custom serialization logic in the class. It is part of the custom serialization process and can override the default serialization behavior.

  6. void writeUnshared(Object obj):
    This writes an object to the stream without attempting to check whether it has been written before in the current stream. It is useful in cases where multiple references to the same object need to be serialized independently.


Using the Java ObjectOutputStream Class: Practical Examples

Example 1: Serializing a Simple Object to a File

This is the most common use case for ObjectOutputStream—serializing an object to a file.

import java.io.*;

class Person implements Serializable {
    String name;
    int age;

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

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

public class ObjectOutputStreamExample {
    public static void main(String[] args) {
        Person person = new Person("Alice", 30);
        
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person.ser"))) {
            oos.writeObject(person);  // Serialize the person object
            System.out.println("Person object serialized successfully.");
        } catch (IOException e) {
            System.out.println("An error occurred during serialization.");
            e.printStackTrace();
        }
    }
}

Explanation:

  • The Person class implements the Serializable interface, allowing its instances to be serialized.
  • The ObjectOutputStream is used to write the person object to a file named person.ser.
  • The writeObject() method is used to serialize the object and save it to the file.

Output:

Person object serialized successfully.

Example 2: Serializing Multiple Objects

You can use ObjectOutputStream to serialize multiple objects to a stream.

import java.io.*;

class Person implements Serializable {
    String name;
    int age;

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

public class SerializeMultipleObjects {
    public static void main(String[] args) {
        Person person1 = new Person("John", 25);
        Person person2 = new Person("Mary", 28);
        
        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("people.ser"))) {
            oos.writeObject(person1);  // Serialize the first person object
            oos.writeObject(person2);  // Serialize the second person object
            System.out.println("Two person objects serialized successfully.");
        } catch (IOException e) {
            System.out.println("An error occurred during serialization.");
            e.printStackTrace();
        }
    }
}

Explanation:

  • Two Person objects are created and serialized to the people.ser file.
  • Each object is written to the stream using writeObject().

Output:

Two person objects serialized successfully.

Example 3: Custom Serialization Logic

You can implement custom serialization behavior by overriding the writeObject() method in your class.

import java.io.*;

class Person implements Serializable {
    String name;
    transient int age;  // transient fields are not serialized by default

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

    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject();  // Serialize default fields
        oos.writeInt(age * 2);  // Custom serialization logic: writing age * 2
    }

    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

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

        try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("person_custom.ser"))) {
            oos.writeObject(person);  // Serialize the person object with custom logic
            System.out.println("Person object with custom serialization serialized successfully.");
        } catch (IOException e) {
            System.out.println("An error occurred during serialization.");
            e.printStackTrace();
        }
    }
}

Explanation:

  • The Person class has a transient field age, meaning it will not be serialized by default.
  • The writeObject() method is overridden to customize the serialization of the age field. Here, the age is serialized as age * 2 instead of its original value.

Output:

Person object with custom serialization serialized successfully.

Advantages of Using ObjectOutputStream

  1. Easy Serialization of Objects: ObjectOutputStream makes it easy to write serialized objects to files or network streams. This is particularly useful for storing objects in files or transmitting them between different components of a system.
  2. Support for Custom Serialization: You can override the writeObject() method to customize the way an object is serialized. This is useful for handling transient fields, special data formats, or complex objects.
  3. Efficient Handling of Object References: ObjectOutputStream handles object references, ensuring that objects are written once even if they are referenced multiple times in the object graph.