ZetCode

Java InvalidObjectException 类

最后修改时间:2025 年 4 月 16 日

当验证失败时,在对象反序列化期间会抛出 java.io.InvalidObjectException。它表明反序列化的对象未能通过验证检查。此异常通常由 readObject 方法抛出。

InvalidObjectException 扩展了 ObjectStreamException,并且是 Java 序列化机制的一部分。当对象在反序列化后状态无效时使用。该异常通常包含一个详细的消息,解释验证失败的原因。

InvalidObjectException 类概述

InvalidObjectException 是 Java I/O 包中的一个已检查异常。当对象在反序列化期间未能通过其内部一致性检查时抛出。该类提供了构造函数,用于创建带有详细消息的异常。

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

上面的代码展示了 InvalidObjectException 的简单结构。它有一个接受原因字符串的构造函数。此字符串应该解释对象验证在反序列化期间失败的原因。

InvalidObjectException 基本示例

此示例演示了一个简单的情况,即在反序列化期间抛出 InvalidObjectException。我们创建一个类,该类在其 readObject 方法中实现自定义验证。

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();
        }
    }
}

在此示例中,Person 类验证年龄在反序列化期间是否在 0 到 120 之间。当提供无效年龄 (150) 时,readObject 方法抛出 InvalidObjectException。异常消息清楚地说明了验证失败的原因。

使用 InvalidObjectException 进行自定义验证

此示例展示了更复杂的验证逻辑,它检查多个字段及其关系。该异常提供了关于具体哪些内容验证失败的详细信息。

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();
        }
    }
}

BankAccount 类在反序列化期间执行三个验证检查。它验证账号格式,确保透支限额为非负数,并检查余额是否符合透支限额。当任何检查失败时,将抛出一个描述性的 InvalidObjectException

将 InvalidObjectException 与 Externalizable 一起使用

此示例演示了将 InvalidObjectExceptionExternalizable 接口一起使用。验证发生在读取所有字段后的 readExternal 方法中。

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();
        }
    }
}

Product 类实现了 Externalizable 并在 readExternal 中执行验证。它检查价格是否为正数,库存是否为非负数,以及产品 ID 是否为空。每个验证失败都会抛出带有特定错误消息的 InvalidObjectException

使用 InvalidObjectException 进行嵌套对象验证

此示例展示了如何在反序列化期间验证嵌套对象。父对象的验证包括检查其子对象的有效性。

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();
        }
    }
}

在此示例中,CustomerAddress 类都在反序列化期间执行验证。Customer 验证其姓名和地址字段,而 Address 验证其自己的字段。验证失败会级联,并带有详细的错误消息。

优雅地处理 InvalidObjectException

此示例演示了处理 InvalidObjectException 时的正确错误处理方法。它展示了如何从反序列化失败中恢复并提供用户友好的错误消息。

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();
        }
    }
}

此示例展示了一种处理 InvalidObjectException 的实用方法。当配置反序列化由于无效值而失败时,应用程序将回退到默认设置。来自异常的错误消息用于告知用户发生了什么错误。

来源

Java InvalidObjectException 类文档

在本文中,我们涵盖了 Java InvalidObjectException 类的基本方面。理解这个异常对于在 Java 应用程序中实现反序列化期间的健壮的对象验证至关重要。

作者

我的名字是 Jan Bodnar,我是一位经验丰富的程序员,在这个领域有多年经验。我从 2007 年开始撰写编程文章,此后撰写了 1,400 多篇文章和八本电子书。凭借超过八年的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程