ZetCode

Java ObjectInput 接口

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

java.io.ObjectInput 接口继承了 DataInput,并提供了读取对象和基本数据类型的功能。它主要用于 Java 序列化机制中的对象反序列化。

ObjectInput 由诸如 ObjectInputStream 之类的类实现,这些类执行对象的反序列化。 该接口提供了从输入源读取对象、基本类型和数组的方法。它还支持读取字节和跳过数据。

ObjectInput 接口概述

ObjectInput 接口结合了对象读取和基本数据读取功能。 关键方法包括对象反序列化、基本类型读取和流控制操作。 所有方法都可以抛出 IOException 以表示 I/O 错误。

public interface ObjectInput extends DataInput, AutoCloseable {
    Object readObject() throws ClassNotFoundException, IOException;
    int read() throws IOException;
    int read(byte[] b) throws IOException;
    int read(byte[] b, int off, int len) throws IOException;
    long skip(long n) throws IOException;
    int available() throws IOException;
    void close() throws IOException;
}

上面的代码显示了 ObjectInput 的核心方法。 该接口继承了 DataInput,它提供了基本读取方法。 readObject 方法是反序列化对象的主要方法。

基本对象反序列化

ObjectInput 最基本的用途是反序列化对象。 这要求对象的类实现 SerializablereadObject 方法读取并重建序列化的对象。

Main.java
import java.io.FileInputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.IOException;

class Person implements java.io.Serializable {
    private String name;
    private 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 Main {
    public static void main(String[] args) {
        try (ObjectInput in = new ObjectInputStream(
                new FileInputStream("person.ser"))) {
            
            Person p = (Person) in.readObject();
            System.out.println("Deserialized Person: " + p);
            
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了基本对象反序列化。 Person 类必须实现 SerializablereadObject 方法从序列化数据重建对象。 强制转换是必要的,因为 readObject 返回 Object

读取基本类型

ObjectInputDataInput 继承基本类型读取方法。 这些包括读取所有 Java 基本类型的方法。 这些方法以网络字节顺序(大端)读取数据。

Main.java
import java.io.FileInputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        try (ObjectInput in = new ObjectInputStream(
                new FileInputStream("primitives.dat"))) {
            
            boolean bool = in.readBoolean();
            byte b = in.readByte();
            char c = in.readChar();
            short s = in.readShort();
            int i = in.readInt();
            long l = in.readLong();
            float f = in.readFloat();
            double d = in.readDouble();
            
            System.out.println("Boolean: " + bool);
            System.out.println("Byte: " + b);
            System.out.println("Char: " + c);
            System.out.println("Short: " + s);
            System.out.println("Int: " + i);
            System.out.println("Long: " + l);
            System.out.println("Float: " + f);
            System.out.println("Double: " + d);
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例显示了从 ObjectInput 流读取基本类型。 数据必须以写入时的相同顺序读取。 每种方法都读取该类型所需的字节数。 流必须包含足够的数据用于所有读取。

读取字节数组

ObjectInput 提供了读取字节数组的方法。 read(byte[]) 方法尝试填充整个数组。 read(byte[], int, int) 方法将数据读入数组的一部分。

Main.java
import java.io.FileInputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.IOException;
import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        try (ObjectInput in = new ObjectInputStream(
                new FileInputStream("bytes.dat"))) {
            
            // Read full array
            byte[] buffer1 = new byte[10];
            int bytesRead1 = in.read(buffer1);
            System.out.println("Read " + bytesRead1 + " bytes: " + 
                Arrays.toString(buffer1));
            
            // Read partial array
            byte[] buffer2 = new byte[20];
            int bytesRead2 = in.read(buffer2, 5, 10);
            System.out.println("Read " + bytesRead2 + " bytes into offset 5: " + 
                Arrays.toString(buffer2));
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例演示了读取字节数组。 第一次读取尝试填充整个 10 字节数组。 第二次读取将 10 个字节放入 20 字节数组中,从偏移量 5 开始。这两种方法都返回实际读取的字节数。

跳过字节和检查可用性

skip 方法允许跳过输入流中的字节。 available 方法估计可以在不阻塞的情况下读取的字节数。 这些对于流导航和状态检查很有用。

Main.java
import java.io.ByteArrayInputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.IOException;

public class Main {
    public static void main(String[] args) {
        byte[] data = new byte[100];
        for (int i = 0; i < data.length; i++) {
            data[i] = (byte) i;
        }
        
        try (ObjectInput in = new ObjectInputStream(
                new ByteArrayInputStream(data))) {
            
            System.out.println("Initially available: " + in.available());
            
            // Skip first 20 bytes
            long skipped = in.skip(20);
            System.out.println("Skipped " + skipped + " bytes");
            
            // Read next byte
            int nextByte = in.read();
            System.out.println("Next byte: " + nextByte);
            
            System.out.println("Now available: " + in.available());
            
            // Skip remaining
            skipped = in.skip(100);
            System.out.println("Skipped " + skipped + " more bytes");
            
            System.out.println("Final available: " + in.available());
            
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例显示了跳过字节和检查可用性。 skip 方法可能跳过的字节少于请求的字节。 available 方法返回剩余的字节。 跳过所有字节后,available 返回 0。

读取多个对象

ObjectInput 可以从流中按顺序读取多个对象。 每个 readObject 调用都读取下一个对象。 对象必须以写入时的相同顺序读取。

Main.java
import java.io.FileInputStream;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.IOException;

class Product implements java.io.Serializable {
    private String name;
    private double price;
    
    public Product(String name, double price) {
        this.name = name;
        this.price = price;
    }
    
    @Override
    public String toString() {
        return String.format("Product{name='%s', price=%.2f}", name, price);
    }
}

public class Main {
    public static void main(String[] args) {
        try (ObjectInput in = new ObjectInputStream(
                new FileInputStream("products.ser"))) {
            
            // Read first object
            Product p1 = (Product) in.readObject();
            System.out.println("First product: " + p1);
            
            // Read second object
            Product p2 = (Product) in.readObject();
            System.out.println("Second product: " + p2);
            
            // Read primitive between objects
            int count = in.readInt();
            System.out.println("Product count: " + count);
            
            // Read third object
            Product p3 = (Product) in.readObject();
            System.out.println("Third product: " + p3);
            
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

此示例读取与基本数据穿插的多个对象。 对象和基本类型必须以写入时的完全相同的顺序读取。 尝试以错误的顺序读取对象将导致异常。 该流维护序列化图结构。

来源

Java ObjectInput 接口文档

在本文中,我们介绍了 Java ObjectInput 接口的基本方法和功能。 理解这些概念对于在 Java 应用程序中使用对象序列化和反序列化至关重要。

作者

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

列出所有Java教程