ZetCode

Java StreamCorruptedException 类

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

当流头无效或从流中读取的控制信息违反一致性检查时,将抛出 java.io.StreamCorruptedException 异常。它表示对象序列化流中的损坏。

此异常通常在从 ObjectInputStream 读取对象进行反序列化时发生。它表明流数据已损坏或写入不正确。该异常扩展了 ObjectStreamException

StreamCorruptedException 类概述

StreamCorruptedException 是 Java 对象序列化机制的一部分。它包含用于创建带有或不带有详细消息的异常的构造函数。该类从其层次结构继承了标准的异常功能。

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

上面的代码显示了 StreamCorruptedException 的简单结构。无参数的构造函数创建一个没有消息的异常。第二个构造函数允许指定详细的错误消息,解释损坏原因。

基本的流损坏示例

此示例演示了 StreamCorruptedException 发生的简单情况。我们将通过修改其标头来故意破坏一个序列化流。该异常有助于及早检测无效的序列化流。

Main.java
import java.io.*;

public class Main {

    public static void main(String[] args) {
        try {
            // Create a byte array output stream
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            
            // Write a simple string
            oos.writeObject("Hello, World!");
            oos.close();
            
            // Get the bytes and corrupt the stream header
            byte[] data = baos.toByteArray();
            data[0] = 0; // Corrupt the magic number
            
            // Try to read the corrupted stream
            ByteArrayInputStream bais = new ByteArrayInputStream(data);
            ObjectInputStream ois = new ObjectInputStream(bais);
            
            // This will throw StreamCorruptedException
            String s = (String) ois.readObject();
            ois.close();
            
        } catch (StreamCorruptedException e) {
            System.err.println("Stream corrupted: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在此示例中,我们首先将一个字符串序列化为字节数组。然后,我们通过更改第一个字节来故意破坏流标头。当尝试反序列化时,无效的标头会触发 StreamCorruptedException

对象流版本不匹配

此示例演示了序列化和反序列化之间的版本不匹配如何导致 StreamCorruptedException。不同的 JVM 或序列化版本可能会产生不兼容的流。

Main.java
import java.io.*;

public class Main {

    static class Data implements Serializable {
        private static final long serialVersionUID = 1L;
        String value;
        
        Data(String value) {
            this.value = value;
        }
    }

    public static void main(String[] args) {
        try {
            // Serialize with one version UID
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(new Data("Test"));
            oos.close();
            
            // Get bytes and modify version UID in the stream
            byte[] data = baos.toByteArray();
            // Simulate version change by altering serialVersionUID bytes
            data[20] = (byte) ~data[20];
            
            // Attempt deserialization
            ByteArrayInputStream bais = new ByteArrayInputStream(data);
            ObjectInputStream ois = new ObjectInputStream(bais);
            
            // This throws StreamCorruptedException
            Data d = (Data) ois.readObject();
            ois.close();
            
        } catch (StreamCorruptedException e) {
            System.err.println("Version mismatch detected: " + e);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在这里,我们通过更改流中的 serialVersionUID 来模拟版本不匹配。反序列化失败,出现 StreamCorruptedException,因为修改后的版本与类定义不匹配。这可以防止出现不兼容的序列化数据。

无效的流标头格式

此示例演示了无效的流标头格式如何导致 StreamCorruptedException。标头包含必须与预期值匹配的魔数和版本信息。

Main.java
import java.io.*;

public class Main {

    public static void main(String[] args) {
        try {
            // Create a valid serialized stream
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject("Valid data");
            oos.close();
            
            // Prepend invalid header bytes
            byte[] validData = baos.toByteArray();
            byte[] invalidData = new byte[validData.length + 4];
            System.arraycopy(validData, 0, invalidData, 4, validData.length);
            
            // Try to deserialize
            ByteArrayInputStream bais = new ByteArrayInputStream(invalidData);
            ObjectInputStream ois = new ObjectInputStream(bais);
            
            // Throws StreamCorruptedException due to invalid header
            String s = (String) ois.readObject();
            ois.close();
            
        } catch (StreamCorruptedException e) {
            System.err.println("Invalid stream header: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

我们创建一个有效的序列化流,然后添加额外的字节来破坏标头格式。ObjectInputStream 立即检测到无效标头并抛出 StreamCorruptedException。这可以防止处理格式错误的流。

截断流检测

此示例演示了 StreamCorruptedException 如何帮助检测截断的流。不完整的序列化数据会在反序列化期间导致一致性检查失败。

Main.java
import java.io.*;

public class Main {

    public static void main(String[] args) {
        try {
            // Serialize multiple objects
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject("First object");
            oos.writeObject("Second object");
            oos.close();
            
            // Truncate the stream data
            byte[] fullData = baos.toByteArray();
            byte[] truncatedData = new byte[fullData.length / 2];
            System.arraycopy(fullData, 0, truncatedData, 0, truncatedData.length);
            
            // Attempt deserialization
            ByteArrayInputStream bais = new ByteArrayInputStream(truncatedData);
            ObjectInputStream ois = new ObjectInputStream(bais);
            
            // First object reads fine
            String first = (String) ois.readObject();
            System.out.println("Read: " + first);
            
            // Second object throws StreamCorruptedException
            String second = (String) ois.readObject();
            ois.close();
            
        } catch (StreamCorruptedException e) {
            System.err.println("Truncated stream detected: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

我们序列化两个对象,然后截断字节数组。第一个对象成功反序列化,但尝试读取第二个对象会抛出 StreamCorruptedException。该异常表明流在反序列化期间意外结束。

自定义序列化损坏

此示例演示了无效的自定义序列化如何导致 StreamCorruptedExceptionwriteObjectreadObject 方法必须保持一致性。

Main.java
import java.io.*;

public class Main {

    static class CustomData implements Serializable {
        private String value;
        
        CustomData(String value) {
            this.value = value;
        }
        
        private void writeObject(ObjectOutputStream out) throws IOException {
            out.defaultWriteObject();
            out.writeInt(123); // Write extra data
        }
        
        private void readObject(ObjectInputStream in) 
            throws IOException, ClassNotFoundException {
            in.defaultReadObject();
            // Forget to read the extra data
        }
    }

    public static void main(String[] args) {
        try {
            // Serialize
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(new CustomData("Test"));
            oos.close();
            
            // Deserialize
            ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
            ObjectInputStream ois = new ObjectInputStream(bais);
            
            // Throws StreamCorruptedException due to unread data
            CustomData cd = (CustomData) ois.readObject();
            ois.close();
            
        } catch (StreamCorruptedException e) {
            System.err.println("Custom serialization error: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

CustomData 类在 writeObject 中写入额外的数据,但不在 readObject 中读取它。这种不一致会导致反序列化期间出现 StreamCorruptedException。该异常有助于捕获序列化/反序列化不匹配。

网络流损坏

此示例模拟可能导致 StreamCorruptedException 的网络流损坏。网络问题会在传输过程中损坏序列化数据。

Main.java
import java.io.*;
import java.util.Arrays;

public class Main {

    public static void main(String[] args) {
        try {
            // Simulate network transmission with potential corruption
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(Arrays.asList("A", "B", "C"));
            oos.close();
            
            // Simulate network corruption by flipping some bits
            byte[] transmitted = baos.toByteArray();
            for (int i = 10; i < 20; i++) {
                transmitted[i] = (byte) ~transmitted[i];
            }
            
            // Attempt deserialization
            ByteArrayInputStream bais = new ByteArrayInputStream(transmitted);
            ObjectInputStream ois = new ObjectInputStream(bais);
            
            // Throws StreamCorruptedException due to corrupted data
            java.util.List<?> list = (java.util.List<?>) ois.readObject();
            ois.close();
            
            System.out.println("Read list: " + list);
            
        } catch (StreamCorruptedException e) {
            System.err.println("Network corruption detected: " + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

我们通过翻转序列化数据中的位来模拟网络损坏。ObjectInputStream 检测到损坏的数据并抛出 StreamCorruptedException。在实际应用中,校验和或重传将处理此类错误。

来源

Java StreamCorruptedException 类文档

在本文中,我们探讨了可能导致 Java 中 StreamCorruptedException 的各种场景。了解这些情况有助于开发可靠的序列化代码和适当的错误处理。

作者

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

列出所有Java教程