从文件中读取对象和数据时,Java如何知道对象和数据的类型

时间:2015-01-27 06:42:42

标签: java input

您好我是Java和学习的新手,我在这里搜索了这个问题的答案并用Google搜索,查看了Java Doc但似乎无法找到关于这个主题的明确解释。

当包装流和Serializable对象时,将对象,数据,基元的混合写入文件,然后再读回它们,数据,对象,基元必须以与写入时相同的顺序读回。如果类型或错误序列不匹配,则会出现EOFException。

在回读文件时,Java如何知道对象和数据的类型?我只能断定这是存储在文件中,还是FileDescriptor?但是在Java文档中没有任何相关内容?或者在那里?

编辑:我已经测试了这个(Java 8),如果我的序列错误,我会得到一个EOFException,并且只是在读取文件时才得到Exception。

EDIT2:对不起我没有发布代码,有点丢失它,因为我做了很多不同的流写入和读取,并在开始读取文件时纠正了类型错误带走了异常,多次得到EOFException。代码如下,必须从内存中重新创建。

try (DataOutputStream dataOut = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(dataFile)))) {

        for (int i = 0; i < prices.length; i++) {
            dataOut.writeDouble(prices[i]);
            dataOut.write(numbers[i]);
            dataOut.writeUTF(desc[i]);
        }

 try (DataInputStream dataIn = new DataInputStream(new BufferedInputStream(
            new FileInputStream(dataFile)));) {
        double price;
        int number;
        String description;
        int c = 0;
        while (true) {
            price = dataIn.readDouble();
            number = dataIn.readInt();
            description = dataIn.readUTF();
            System.out.println(price + " " + number + " " + description);

我省略了一些代码,但核心元素在那里,所以readInt方法产生一个EOFException,(使用的是write方法,而不是writeInt)。如果我更正错误(更改为writeInt)一切正常。但是,如果我在阅读时在价格和数字之间交换位置没有错误并打印出垃圾,但仍然正常完成。

while (true) {
            description = dataIn.readUTF();
            price = dataIn.readDouble();
            number = dataIn.readInt();

但是如果我在读取文件时移动readUTF,我又会再次获得EOFException。

1 个答案:

答案 0 :(得分:51)

  

如果类型或错误序列不匹配,则会出现EOFException。

不,这根本不是真的 - 至少在一般情况下不是这样。如果您过早地到达数据末尾,您将获得EOFException。如果您只是使用错误的方法读取数据,但仍然读取相同数量的数据(例如,两次调用readInt而不是readLong一次),您将获得无用的数据,但也不例外。 (对此有豁免 - 如果您使用readUTF,如果数据无效,则会出现例外修改UTF-8。或者readUTF 可以抛出如果长度前缀表示字符串比流中的数据长,那么EOFException - 再次,这是读取流的末尾的问题。)

这是一个完整的例子:

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
        byte[] data;
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            try (DataOutputStream dos = new DataOutputStream(baos)) {
                dos.writeLong(123456789012345L);
            }
            data = baos.toByteArray();
        }

        try (ByteArrayInputStream bais = new ByteArrayInputStream(data)) {
            try (DataInputStream dis = new DataInputStream(bais)) {
                System.out.println(dis.readInt()); // 28744
                System.out.println(dis.readInt()); // -2045911175
            }
        }
    }
}

请注意,在使用ObjectOutputStream时,当您致电writeObject 将类型名称作为数据的一部分写入,以便在您致电readObject时它知道要创建什么样的对象。但是,原始读/写方法(例如上面使用的方法)仍然没有留下任何数据类型的指示。在上面的代码中将DataObjectStream / DataInputStream更改为ObjectOutputStream / ObjectInputStream不会改变结果。

如果你调用readObject,并且流被放置在对象数据的开头,它仍然不会抛出EOFException - 它只会读取正确类型的对象,如果你施放错误的类型你会得到一个ClassCastException。 (没有办法说“我希望现在就读Foo。”)如果你打电话给readObject并且 然后根据 存在的数据,可能发生任何奇怪的事情。

如果您尝试阅读数据末尾,则只会获得EOFException,但是您这样做了 - 例如撰写long,然后阅读三个 int值。

作为readUTF如何导致EOFException的示例,请考虑以下代码:

import java.io.*;

public class Test {
    public static void main(String[] args) throws IOException {
                byte[] data;
        try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
            try (DataOutputStream dos = new DataOutputStream(baos)) {
                dos.writeShort(10000);
                for (int i = 0; i < 500; i++) {
                    dos.write(0);
                }
            }
            data = baos.toByteArray();
        }

        try (ByteArrayInputStream bais = new ByteArrayInputStream(data)) {
            try (DataInputStream dis = new DataInputStream(bais)) {
                System.out.println(dis.readUTF());
            }
        }
    }
}

我们开始编写short值10000,然后我们写500个其他字节。当我们调用readUTF时,它将读取10000的值,然后期望能够读取10000多个字节,因为这是格式readUTF期望的(长度,数据)。由于没有足够的数据, 将失败并显示EOFException。如果您将writeShort(10000)更改为writeShort(100),那么异常就会消失 - 即使您仍然没有将写入与读取匹配。