ZetCode

Java ObjectInputValidation 接口

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

java.io.ObjectInputValidation 接口为反序列化期间验证对象提供了一种回调机制。 它允许对象在整个对象图重建后但在反序列化过程完成之前执行验证。

实现此接口使对象能够在反序列化后检查其状态有效性。 当调用 ObjectInputStream.registerValidation 时,会发生验证。 这对于维护对象不变性特别有用。

ObjectInputValidation 接口概述

该接口包含一个必须实现的单个方法 validateObject。 此方法由序列化机制调用以执行验证。 如果验证失败,它应该抛出一个 InvalidObjectException

public interface ObjectInputValidation {
    void validateObject() throws InvalidObjectException;
}

上面的代码显示了 ObjectInputValidation 接口的简单结构。 类实现此接口以添加验证逻辑,该逻辑在反序列化完成后但在将对象返回给调用者之前执行。

基本验证示例

此示例演示了 ObjectInputValidation 的一个简单实现。 我们创建一个 Person 类,该类在反序列化后验证其年龄字段。 验证确保年龄在合理范围内。

Person.java
import java.io.*;
import java.util.Objects;

public class Person implements Serializable, ObjectInputValidation {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = Objects.requireNonNull(name);
        this.age = age;
    }
    
    private void readObject(ObjectInputStream ois) 
            throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        ois.registerValidation(this, 0); // Register for validation
    }
    
    @Override
    public void validateObject() throws InvalidObjectException {
        if (age < 0 || age > 150) {
            throw new InvalidObjectException("Invalid age: " + age);
        }
        if (name == null || name.trim().isEmpty()) {
            throw new InvalidObjectException("Name cannot be empty");
        }
    }
    
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + "}";
    }
}

Person 类同时实现了 SerializableObjectInputValidationreadObject 方法注册验证回调。 validateObject 方法检查年龄和名称的有效性。 如果无效,它会抛出 InvalidObjectException

复杂对象图验证

此示例显示了在具有关系的更复杂对象图中的验证。 我们创建 DepartmentEmployee 类,其中部门验证其反序列化后的员工列表。

Department.java
import java.io.*;
import java.util.*;

public class Department implements Serializable, ObjectInputValidation {
    private String name;
    private List<Employee> employees = new ArrayList<>();
    
    public Department(String name) {
        this.name = Objects.requireNonNull(name);
    }
    
    public void addEmployee(Employee emp) {
        employees.add(Objects.requireNonNull(emp));
    }
    
    private void readObject(ObjectInputStream ois) 
            throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        ois.registerValidation(this, 0);
    }
    
    @Override
    public void validateObject() throws InvalidObjectException {
        if (name == null || name.trim().isEmpty()) {
            throw new InvalidObjectException("Department name is invalid");
        }
        if (employees.isEmpty()) {
            throw new InvalidObjectException("Department must have employees");
        }
        for (Employee emp : employees) {
            if (emp.getDepartment() != this) {
                throw new InvalidObjectException("Employee department mismatch");
            }
        }
    }
    
    // Getters and toString omitted for brevity
}
Employee.java
import java.io.*;

public class Employee implements Serializable {
    private String name;
    private Department department;
    
    public Employee(String name, Department department) {
        this.name = Objects.requireNonNull(name);
        this.department = Objects.requireNonNull(department);
        department.addEmployee(this);
    }
    
    public Department getDepartment() {
        return department;
    }
    
    // Other methods omitted
}

此示例演示了对象关系的验证。 Department 验证它是否有员工,并且每个员工都指向它。 验证在整个对象图反序列化后发生。 这确保所有引用在验证之前都已正确建立。

基于优先级的验证

此示例演示了如何在 registerValidation 中使用优先级参数。 优先级较高的验证器在优先级较低的验证器之前运行。 我们创建一个系统,其中组件验证发生在系统验证之前。

SystemComponent.java
import java.io.*;
import java.util.*;

public class SystemComponent implements Serializable, ObjectInputValidation {
    private String id;
    private SystemConfiguration config;
    
    public SystemComponent(String id, SystemConfiguration config) {
        this.id = Objects.requireNonNull(id);
        this.config = Objects.requireNonNull(config);
        config.addComponent(this);
    }
    
    private void readObject(ObjectInputStream ois) 
            throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        // Register with high priority (1)
        ois.registerValidation(this, 1);
    }
    
    @Override
    public void validateObject() throws InvalidObjectException {
        System.out.println("Validating component " + id);
        if (id.trim().isEmpty()) {
            throw new InvalidObjectException("Component ID cannot be empty");
        }
    }
}
SystemConfiguration.java
import java.io.*;
import java.util.*;

public class SystemConfiguration implements Serializable, ObjectInputValidation {
    private List<SystemComponent> components = new ArrayList<>();
    
    public void addComponent(SystemComponent component) {
        components.add(component);
    }
    
    private void readObject(ObjectInputStream ois) 
            throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        // Register with lower priority (0)
        ois.registerValidation(this, 0);
    }
    
    @Override
    public void validateObject() throws InvalidObjectException {
        System.out.println("Validating system configuration");
        if (components.isEmpty()) {
            throw new InvalidObjectException("No components in system");
        }
        // Additional validation logic
    }
}

在此示例中,由于组件的优先级更高(1 与 0),因此组件在系统配置之前进行验证。 这确保组件级别的验证在系统级别的验证开始之前完成。 优先级参数控制验证顺序。

来源

Java ObjectInputValidation 接口文档

在本文中,我们介绍了 Java ObjectInputValidation 接口的基本概念和特性。 了解这种机制对于在 Java 应用程序中实现健壮的反序列化验证至关重要。

作者

我叫 Jan Bodnar,是一位经验丰富的专注程序员。 我于 2007 年开始撰写编程文章,至今已撰写了 1,400 多篇文章和八本电子书。 凭借八年多的教学经验,我致力于分享我的知识并帮助他人掌握编程概念。

列出所有Java教程