Java ObjectInputStream Class


In Java, the ObjectInputStream class, part of the java.io package, is used for reading serialized objects from an input stream. It allows you to deserialize data, meaning it converts the byte stream into a Java object that you can manipulate in your application. Serialization is a mechanism to convert an object into a byte stream, and ObjectInputStream provides a way to read these byte streams back into objects.

Serialization and deserialization are common techniques used for object persistence (saving objects to files or sending objects over a network). ObjectInputStream is primarily used to read objects that were previously serialized using ObjectOutputStream.


What is the ObjectInputStream Class?

The ObjectInputStream class is a subclass of InputStream, which is used for reading serialized objects from a stream. When you write an object to an output stream (like using ObjectOutputStream), the object is converted into a byte stream. The ObjectInputStream class reads this byte stream and converts it back into the original object.

The primary use case for ObjectInputStream is for deserializing objects. Deserialization allows an application to reconstruct objects that were previously saved or transmitted.


Key Methods of the ObjectInputStream Class

The ObjectInputStream class inherits methods from InputStream but also provides unique methods for reading and deserializing objects. Here are the most important ones:

  1. Object readObject():
    This method is the core function of ObjectInputStream. It reads the next object from the input stream and returns it. The object is deserialized into its original state.

    Object obj = objectInputStream.readObject();
    
  2. int read():
    Reads a single byte of data from the input stream. This is inherited from the InputStream class and can be used for general byte reading.

  3. void close():
    Closes the stream and releases any system resources associated with it. It is important to close the stream after it is no longer needed.

  4. void defaultReadObject():
    This method is used when you want to read non-static and non-transient fields from the stream. It's typically used in the context of custom deserialization logic (e.g., when implementing the readObject() method in your classes).

  5. long skip(long n):
    Skips over n bytes of data in the input stream, returning the number of bytes actually skipped.

  6. int available():
    Returns the number of bytes that can be read from the stream without blocking. This can be useful for checking if data is available to read.

  7. void enableResolveObject(boolean resolve):
    This method controls whether or not to resolve object references during the deserialization process.


Using the Java ObjectInputStream Class: Practical Examples

Example 1: Deserializing a Simple Object

The following example demonstrates how to deserialize a simple object using ObjectInputStream. In this case, the object is a simple Person object that was previously serialized.

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 ObjectInputStreamExample {
    public static void main(String[] args) {
        // Assuming the object was previously serialized and saved in "person.ser"
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
            Person person = (Person) ois.readObject();  // Read and deserialize the object
            System.out.println(person);  // Output the deserialized object
        } catch (IOException | ClassNotFoundException e) {
            System.out.println("An error occurred during deserialization.");
            e.printStackTrace();
        }
    }
}

Explanation:

  • A Person class implements the Serializable interface, indicating that instances of this class can be serialized.
  • The ObjectInputStream is used to read the object from a file called person.ser.
  • The readObject() method deserializes the object and returns it.
  • The deserialized object is then printed.

Note: This example assumes that the object was serialized previously using ObjectOutputStream.

Example 2: Handling Custom Serialization

When dealing with custom serialization, you can define your own deserialization logic by implementing the readObject() method in your class. This gives you more control over how an object is deserialized.

import java.io.*;

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

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

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();  // Read default fields
        this.age = 30;  // Set default value for age after deserialization
    }

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

public class CustomDeserializationExample {
    public static void main(String[] args) {
        // Assuming the object was serialized in a previous session
        try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("person.ser"))) {
            Person person = (Person) ois.readObject();
            System.out.println(person);  // Output the deserialized object with custom deserialization logic
        } catch (IOException | ClassNotFoundException e) {
            System.out.println("An error occurred during deserialization.");
            e.printStackTrace();
        }
    }
}

Explanation:

  • The Person class has a transient field age, which means it won't be serialized by default.
  • A readObject() method is implemented to manually handle the deserialization of the object. It uses defaultReadObject() to read the non-transient fields and sets a default value for the age field.
  • This allows you to control how deserialized objects behave.

Example 3: Reading Objects from a Network Stream

In some scenarios, you may deserialize objects that are being sent over a network. Below is an example of deserializing an object received via a socket connection.

import java.io.*;
import java.net.*;

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 NetworkDeserializationExample {
    public static void main(String[] args) {
        try (ServerSocket serverSocket = new ServerSocket(12345)) {
            System.out.println("Server is waiting for connection...");
            try (Socket socket = serverSocket.accept();
                 ObjectInputStream ois = new ObjectInputStream(socket.getInputStream())) {

                Person person = (Person) ois.readObject();  // Deserialize object from network stream
                System.out.println("Received object: " + person);
            } catch (IOException | ClassNotFoundException e) {
                System.out.println("An error occurred during deserialization.");
                e.printStackTrace();
            }
        } catch (IOException e) {
            System.out.println("An error occurred while setting up the server.");
            e.printStackTrace();
        }
    }
}

Explanation:

  • This example demonstrates deserializing an object received via a ServerSocket. The server listens for incoming connections and deserializes the received object using ObjectInputStream.

Note: This example assumes that an object was serialized and sent from a client to the server.


Advantages of Using ObjectInputStream

  1. Deserialization of Objects: The ObjectInputStream allows you to restore an object from its serialized form, which is useful for object persistence and network communication.
  2. Supports Custom Deserialization: You can customize the deserialization process for specific needs using methods like readObject() and defaultReadObject().
  3. Works with Serializable Objects: It is specifically designed for working with classes that implement the Serializable interface, making it seamless to handle object-based data.