ZetCode

Java ObjectStreamException 类

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

java.io.ObjectStreamException 是所有特定于对象流类的异常的超类。 它指示对象序列化或反序列化期间出现的问题。 此异常扩展了 IOException

ObjectStreamException 有几个子类,代表特定的序列化问题。 这些包括 InvalidClassExceptionNotSerializableExceptionStreamCorruptedException。 每个子类处理不同的序列化失败场景。

ObjectStreamException 类概述

ObjectStreamException 是一个抽象类,用作对象流异常的基础。 它提供构造函数来创建带有详细消息的异常。 该类本身没有添加超出标准异常功能的新方法。

public abstract class ObjectStreamException extends IOException {
    protected ObjectStreamException(String classname);
    protected ObjectStreamException();
}

上面的代码显示了 ObjectStreamException 的基本结构。 它有两个构造函数 - 一个带有消息,一个没有。 具体子类为不同的序列化问题提供特定的异常类型。

InvalidClassException 示例

当序列化运行时检测到类存在问题时,会发生 InvalidClassException。 这包括 serialVersionUID 不匹配或无效的类定义。 该异常提供了有关问题类的详细信息。

Main.java
import java.io.*;

class Data implements Serializable {
    private static final long serialVersionUID = 1L;
    private String info;
    
    // Changed class definition after serialization
    private int newField; // Added after initial serialization
}

public class Main {
    public static void main(String[] args) {
        try {
            // Serialize
            ObjectOutputStream out = new ObjectOutputStream(
                new FileOutputStream("data.ser"));
            out.writeObject(new Data());
            out.close();
            
            // Modify class definition (change serialVersionUID)
            // Then attempt deserialization
            ObjectInputStream in = new ObjectInputStream(
                new FileInputStream("data.ser"));
            Data obj = (Data) in.readObject();
            in.close();
        } catch (InvalidClassException e) {
            System.err.println("InvalidClassException: " + e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此示例演示了当类定义更改时如何发生 InvalidClassException。 在序列化对象后,修改类(例如添加字段)可能会在反序列化期间导致此异常。 serialVersionUID 不匹配是一个常见原因。

NotSerializableException 示例

当对象未标记为 Serializable 时,会抛出 NotSerializableException。 序列化图中的所有对象都必须实现 Serializable。 此异常标识不可序列化的类。

Main.java
import java.io.*;

class NonSerializableData {
    private String info = "Cannot serialize me";
}

class Container implements Serializable {
    private NonSerializableData data = new NonSerializableData();
}

public class Main {
    public static void main(String[] args) {
        try {
            ObjectOutputStream out = new ObjectOutputStream(
                new FileOutputStream("container.ser"));
            out.writeObject(new Container());
            out.close();
        } catch (NotSerializableException e) {
            System.err.println("NotSerializableException: " + e.getMessage());
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

此示例显示了尝试序列化包含不可序列化字段的对象时的 NotSerializableExceptionContainer 类是可序列化的,但其 NonSerializableData 字段不是。 该异常在其消息中标识了有问题类。

StreamCorruptedException 示例

StreamCorruptedException 指示流协议已被违反。 当流数据已损坏或被篡改时,会发生这种情况。 该异常表明流不是预期的格式。

Main.java
import java.io.*;

public class Main {
    public static void main(String[] args) {
        try {
            // Create a valid serialized file
            ObjectOutputStream out = new ObjectOutputStream(
                new FileOutputStream("data.ser"));
            out.writeObject("Hello, Serialization!");
            out.close();
            
            // Corrupt the file by appending random data
            RandomAccessFile raf = new RandomAccessFile("data.ser", "rw");
            raf.seek(raf.length());
            raf.writeBytes("CORRUPTED DATA");
            raf.close();
            
            // Attempt to read corrupted file
            ObjectInputStream in = new ObjectInputStream(
                new FileInputStream("data.ser"));
            String obj = (String) in.readObject();
            in.close();
        } catch (StreamCorruptedException e) {
            System.err.println("StreamCorruptedException: " + e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此示例故意损坏序列化文件以演示 StreamCorruptedException。 在创建有效的序列化文件后,我们附加随机数据以损坏它。 尝试反序列化时,由于流格式无效,因此会发生异常。

OptionalDataException 示例

当发现原始数据而不是对象时,在反序列化期间会发生 OptionalDataException。 这通常发生在读取使用自定义序列化方法编写的流时。 该异常指示意外的数据类型。

Main.java
import java.io.*;

class CustomData implements Serializable {
    private void writeObject(ObjectOutputStream out) throws IOException {
        out.writeInt(42); // Write primitive instead of object
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            // Serialize
            ObjectOutputStream out = new ObjectOutputStream(
                new FileOutputStream("custom.ser"));
            out.writeObject(new CustomData());
            out.close();
            
            // Deserialize
            ObjectInputStream in = new ObjectInputStream(
                new FileInputStream("custom.ser"));
            CustomData obj = (CustomData) in.readObject();
            in.close();
        } catch (OptionalDataException e) {
            System.err.println("OptionalDataException: " + e.getMessage());
            System.out.println("Primitive data found: " + e.length + " bytes");
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此示例显示了自定义序列化写入原始数据时的 OptionalDataExceptionCustomData 类直接将整数写入流。 在反序列化期间,系统期望对象,但却找到原始数据。

WriteAbortedException 示例

当原始序列化失败时,在反序列化期间会发生 WriteAbortedException。 它包装了导致写入中止的异常。 这提供了有关序列化失败原因的上下文。

Main.java
import java.io.*;

class ProblematicData implements Serializable {
    private Object nonSerializable = new Object();
}

public class Main {
    public static void main(String[] args) {
        try {
            // Attempt serialization (will fail)
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(baos);
            out.writeObject(new ProblematicData());
            out.close();
        } catch (NotSerializableException e) {
            try {
                // Now attempt to deserialize the partial stream
                ByteArrayInputStream bais = new ByteArrayInputStream(
                    baos.toByteArray());
                ObjectInputStream in = new ObjectInputStream(bais);
                in.readObject();
                in.close();
            } catch (WriteAbortedException wae) {
                System.err.println("WriteAbortedException: " + wae.getMessage());
                System.out.println("Original exception: " + 
                    wae.detail.getMessage());
                wae.printStackTrace();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }
}

此示例通过首先尝试序列化不可序列化的对象来演示 WriteAbortedException。 序列化失败后,我们尝试反序列化部分流。 该异常包含有关导致中止的原始失败的详细信息。

InvalidObjectException 示例

当反序列化对象的验证失败时,会抛出 InvalidObjectException。 这通常发生在 readObject 方法中的自定义验证期间。 它指示对象未通过后处理检查。

Main.java
import java.io.*;

class ValidatedData implements Serializable {
    private int value;
    
    private void readObject(ObjectInputStream in) 
            throws IOException, ClassNotFoundException {
        in.defaultReadObject();
        if (value < 0 || value > 100) {
            throw new InvalidObjectException("Value must be between 0 and 100");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        try {
            // Serialize valid data
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream out = new ObjectOutputStream(baos);
            ValidatedData valid = new ValidatedData();
            valid.value = 50;
            out.writeObject(valid);
            out.close();
            
            // Modify serialized data to make it invalid
            byte[] data = baos.toByteArray();
            // Corrupt the value to be 200 (simulating invalid data)
            data[data.length - 1] = (byte) 200;
            
            // Attempt deserialization
            ByteArrayInputStream bais = new ByteArrayInputStream(data);
            ObjectInputStream in = new ObjectInputStream(bais);
            ValidatedData obj = (ValidatedData) in.readObject();
            in.close();
        } catch (InvalidObjectException e) {
            System.err.println("InvalidObjectException: " + e.getMessage());
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

此示例显示了自定义验证期间的 InvalidObjectExceptionValidatedData 类检查其值是否在 0-100 范围内。 我们通过直接修改序列化的字节来模拟无效数据。 验证在反序列化期间失败。

来源

Java ObjectStreamException 类文档

在本文中,我们介绍了 ObjectStreamException 类及其常见的子类。 了解这些异常对于 Java 应用程序中健壮的对象序列化和反序列化至关重要。

作者

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

列出所有Java教程