ZetCode

Java InvalidObjectException Class

Last modified: April 16, 2025

The java.io.InvalidObjectException is thrown during object deserialization when validation fails. It indicates that a deserialized object failed validation checks. This exception is typically thrown by the readObject method.

InvalidObjectException extends ObjectStreamException and is part of Java's serialization mechanism. It's used when an object's state is invalid after deserialization. The exception usually contains a detailed message explaining the validation failure.

InvalidObjectException Class Overview

InvalidObjectException is a checked exception in Java's I/O package. It's thrown when an object fails its internal consistency checks during deserialization. The class provides constructors to create exceptions with detailed messages.

public class InvalidObjectException extends ObjectStreamException {
    public InvalidObjectException(String reason);
}

The code above shows the simple structure of InvalidObjectException. It has a single constructor that accepts a reason string. This string should explain why the object validation failed during deserialization.

Basic InvalidObjectException Example

This example demonstrates a simple case where InvalidObjectException is thrown during deserialization. We create a class that implements custom validation in its readObject method.

Main.java
import java.io.*;

class Person implements Serializable {
    private String name;
    private int age;

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

    private void readObject(ObjectInputStream ois) 
            throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        if (age < 0 || age > 120) {
            throw new InvalidObjectException("Invalid age value: " + age);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            Person p = new Person("John", 150);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(p);
            oos.close();

            ByteArrayInputStream bais = 
                new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            Person deserialized = (Person) ois.readObject();
        } catch (InvalidObjectException e) {
            System.err.println("Validation failed: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

In this example, the Person class validates that age is between 0 and 120 during deserialization. When an invalid age (150) is provided, the readObject method throws InvalidObjectException. The exception message clearly states the validation failure reason.

Custom Validation with InvalidObjectException

This example shows more complex validation logic that checks multiple fields and their relationships. The exception provides detailed information about what exactly failed validation.

Main.java
import java.io.*;

class BankAccount implements Serializable {
    private String accountNumber;
    private double balance;
    private double overdraftLimit;

    public BankAccount(String accountNumber, double balance, double overdraftLimit) {
        this.accountNumber = accountNumber;
        this.balance = balance;
        this.overdraftLimit = overdraftLimit;
    }

    private void readObject(ObjectInputStream ois) 
            throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        
        if (accountNumber == null || accountNumber.length() != 10) {
            throw new InvalidObjectException(
                "Account number must be exactly 10 characters");
        }
        
        if (overdraftLimit < 0) {
            throw new InvalidObjectException(
                "Overdraft limit cannot be negative");
        }
        
        if (balance < -overdraftLimit) {
            throw new InvalidObjectException(
                "Balance cannot be below overdraft limit");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            BankAccount account = new BankAccount("123456789", -1000, 500);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(account);
            oos.close();

            ByteArrayInputStream bais = 
                new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            BankAccount deserialized = (BankAccount) ois.readObject();
        } catch (InvalidObjectException e) {
            System.err.println("Account validation failed: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

The BankAccount class performs three validation checks during deserialization. It verifies account number format, ensures overdraft limit is non-negative, and checks that balance respects the overdraft limit. When any check fails, a descriptive InvalidObjectException is thrown.

Using InvalidObjectException with Externalizable

This example demonstrates using InvalidObjectException with the Externalizable interface. The validation occurs in the readExternal method after reading all fields.

Main.java
import java.io.*;

class Product implements Externalizable {
    private String id;
    private String name;
    private double price;
    private int stock;

    public Product() {} // Required for Externalizable

    public Product(String id, String name, double price, int stock) {
        this.id = id;
        this.name = name;
        this.price = price;
        this.stock = stock;
    }

    @Override
    public void writeExternal(ObjectOutput out) throws IOException {
        out.writeUTF(id);
        out.writeUTF(name);
        out.writeDouble(price);
        out.writeInt(stock);
    }

    @Override
    public void readExternal(ObjectInput in) 
            throws IOException, ClassNotFoundException {
        id = in.readUTF();
        name = in.readUTF();
        price = in.readDouble();
        stock = in.readInt();

        if (price <= 0) {
            throw new InvalidObjectException("Price must be positive");
        }
        
        if (stock < 0) {
            throw new InvalidObjectException("Stock cannot be negative");
        }
        
        if (id == null || id.isEmpty()) {
            throw new InvalidObjectException("Product ID is required");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            Product product = new Product("", "Laptop", -999.99, -5);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(product);
            oos.close();

            ByteArrayInputStream bais = 
                new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            Product deserialized = (Product) ois.readObject();
        } catch (InvalidObjectException e) {
            System.err.println("Product validation failed: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

The Product class implements Externalizable and performs validation in readExternal. It checks for positive price, non-negative stock, and non-empty product ID. Each validation failure throws InvalidObjectException with a specific error message.

Nested Object Validation with InvalidObjectException

This example shows how to validate nested objects during deserialization. The parent object's validation includes checking the validity of its child objects.

Main.java
import java.io.*;

class Address implements Serializable {
    private String street;
    private String city;
    private String zipCode;

    public Address(String street, String city, String zipCode) {
        this.street = street;
        this.city = city;
        this.zipCode = zipCode;
    }

    private void readObject(ObjectInputStream ois) 
            throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        if (street == null || city == null || zipCode == null) {
            throw new InvalidObjectException("Address fields cannot be null");
        }
    }
}

class Customer implements Serializable {
    private String name;
    private Address address;

    public Customer(String name, Address address) {
        this.name = name;
        this.address = address;
    }

    private void readObject(ObjectInputStream ois) 
            throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        if (name == null || name.isEmpty()) {
            throw new InvalidObjectException("Customer name is required");
        }
        if (address == null) {
            throw new InvalidObjectException("Address is required");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            Customer customer = new Customer("", new Address(null, null, null));
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(customer);
            oos.close();

            ByteArrayInputStream bais = 
                new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            Customer deserialized = (Customer) ois.readObject();
        } catch (InvalidObjectException e) {
            System.err.println("Customer validation failed: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

In this example, both Customer and Address classes perform validation during deserialization. The Customer validates its name and address fields, while Address validates its own fields. The validation failures cascade with detailed error messages.

Handling InvalidObjectException Gracefully

This example demonstrates proper error handling when dealing with InvalidObjectException. It shows how to recover from deserialization failures and provide user-friendly error messages.

Main.java
import java.io.*;

class Configuration implements Serializable {
    private String environment;
    private int timeout;
    private boolean debugMode;

    public Configuration(String environment, int timeout, boolean debugMode) {
        this.environment = environment;
        this.timeout = timeout;
        this.debugMode = debugMode;
    }

    private void readObject(ObjectInputStream ois) 
            throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        if (!"prod".equals(environment) && !"dev".equals(environment) {
            throw new InvalidObjectException(
                "Invalid environment: " + environment);
        }
        if (timeout < 1 || timeout > 300) {
            throw new InvalidObjectException(
                "Timeout must be between 1 and 300 seconds");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        String filename = "config.ser";
        
        try {
            // Try to deserialize configuration
            Configuration config = loadConfiguration(filename);
            System.out.println("Configuration loaded successfully");
        } catch (InvalidObjectException e) {
            System.err.println("Invalid configuration: " + e.getMessage());
            System.out.println("Loading default configuration instead");
            Configuration defaultConfig = new Configuration("dev", 30, false);
            // Use default configuration...
        } catch (Exception e) {
            System.err.println("Error loading configuration: " + e.getMessage());
        }
    }

    private static Configuration loadConfiguration(String filename) 
            throws IOException, ClassNotFoundException {
        try (ObjectInputStream ois = 
                new ObjectInputStream(new FileInputStream(filename))) {
            return (Configuration) ois.readObject();
        }
    }
}

This example shows a practical approach to handling InvalidObjectException. When configuration deserialization fails due to invalid values, the application falls back to default settings. The error message from the exception is used to inform the user about what went wrong.

Source

Java InvalidObjectException Class Documentation

In this article, we've covered the essential aspects of the Java InvalidObjectException class. Understanding this exception is crucial for implementing robust object validation during deserialization in Java applications.

Author

My name is Jan Bodnar, and I am a dedicated programmer with many years of experience in the field. I began writing programming articles in 2007 and have since authored over 1,400 articles and eight e-books. With more than eight years of teaching experience, I am committed to sharing my knowledge and helping others master programming concepts.

List all Java tutorials.