【问题标题】:Java - Serialization - EOFException IssueJava - 序列化 - EOFException 问题
【发布时间】:2014-09-09 15:33:14
【问题描述】:

抱歉,今天发布了关于序列化的第二篇文章,修复一个问题导致另一个问题。


正如此处所述 - Java - Serialization - NotSerializableException Issue - 我有一个包含以下课程的项目

学生.java

StudentsCollection.java

Students 创建我的 Student 对象(自我解释),并且我的 StudentsCollection() 实例化一个 Student 类型的列表,该列表存储我的 Student 对象,当我尝试保存/加载对象时,我使用此代码并引发以下异常:

       /**
         * Open student collection
         */
       public void openCollection(){
          try {
             FileInputStream e = new FileInputStream("students.ser");
             ObjectInputStream inputSteam = new ObjectInputStream(e);
              while(inputSteam.readObject() != null){
                  this.list.add((Students)inputSteam.readObject());
              }
          } catch (FileNotFoundException var3) {
              var3.printStackTrace();
             JOptionPane.showMessageDialog(null, "File not found");
          } catch (IOException var4) {
              var4.printStackTrace();
             JOptionPane.showMessageDialog(null, "IO Exception");
          } catch (ClassNotFoundException var5) {
              var5.printStackTrace();
             JOptionPane.showMessageDialog(null, "Required class not found");
          }
    
       }

并且正在投掷:

java.io.EOFException
    at java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2598)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1318)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
    at jdatabase.objects.students.StudentsCollection.openCollection(StudentsCollection.java:558)
    at jdatabase.main.MainController.main(MainController.java:14)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

我只有一个 Student 对象添加到我的列表中,保存列表并重新打开它后,我要求控制台打印出该列表,Student 实际打印出来。但是,当我创建多个学生对象并每次将它们的 ID 增加 1 并添加它们时,控制台会按顺序打印它们,然后重新打印(出于某种奇怪的原因)并最终跳过一些。

如果您需要更多代码,请询问。 saveCollection() 现在工作正常

修改后的代码: /**

     * Open student collection
     */
   public void openCollection(){
      try {
         FileInputStream e = new FileInputStream("students.ser");
         ObjectInputStream inputSteam = new ObjectInputStream(e);
          while((obj = inputSteam.readObject()) != null){
              this.list.add((Students)obj);
          }
      } catch (FileNotFoundException var3) {
          var3.printStackTrace();
         JOptionPane.showMessageDialog(null, "File not found");
      } catch (IOException var4) {
          var4.printStackTrace();
         JOptionPane.showMessageDialog(null, "IO Exception");
      } catch (ClassNotFoundException var5) {
          var5.printStackTrace();
         JOptionPane.showMessageDialog(null, "Required class not found");
      }

   }

抛出:

java.io.EOFException
    at java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2598)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1318)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:370)
    at jdatabase.objects.students.StudentsCollection.openCollection(StudentsCollection.java:559)
    at jdatabase.main.MainController.main(MainController.java:13)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)

只有 1 个学生对象,并且在控制台中打印以下内容:

学生姓名:学生 学生姓氏:默认 学生证:0 学生 出生日期:90 年 1 月 1 日

并再次打印:

学生姓名:学生 学生姓氏:默认 学生证:0 学生出生日期:90 年 1 月 1 日

【问题讨论】:

    标签: java serialization io


    【解决方案1】:

    readObject() 不会在流结束时返回null,因此在循环终止条件时对其进行测试是无效的。您的代码正确在遇到流结束时抛出 EOFException。这里没有问题要解决。

    【讨论】:

    • 啊.. 多年前的问题。您的回答现在非常有意义,而且确实正确。 :-)
    【解决方案2】:

    您没有关闭输入流。我的猜测是你没有关闭输出流,它被截断了。使用“try-with-resource”。

          try {
              FileOutputStream e = new FileOutputStream("students.ser");
              ObjectOutputStream outputStream = new ObjectOutputStream(e);
    

    变成

          try (
              FileOutputStream e = new FileOutputStream("students.ser")
              ObjectOutputStream outputStream = new ObjectOutputStream(e);
          ) {
    

    (编辑到 flush 包装流(您不必一直关闭它,但这可能是最好的,而不是冒着发生常见编程错误的风险)。)

    那或者你没有写终止null

    【讨论】:

    • 什么终止null
    • @EJP 使用while(inputSteam.readObject() != null){ 阅读的那个。请注意,这是一个奇怪的方案,它会写入一些丢弃的对象来标记非终止。
    • 他没有写一个终止的空值。因此,没有终止的空值。我不明白你的最后一句话。
    • @EJP 在我回答问题时,尚未添加写入代码。一个合理的序列化方案(不记得是否在Java库中使用过)是写一个null。由于读取的代码试图检查 null,因此有理由怀疑 null 是有意的,但不是出于某种原因而编写的。
    • 现在你自相矛盾了。一个方案不能同时是“奇怪的”和“合理的”。您的“合理”方案可防止您在任何其他时间写入空值。鉴于 EOFException 已经被抛出,null 是多余的。
    【解决方案3】:

    问题是您的读取循环不知道要读取多少对象,因此它会在文件末尾运行并获取EOFException

    阅读代码是这样做的:

    while (inputStream.readObject() != null) ...
    

    问题在于 readObject 从不 返回 null。 (根据 EJP 的评论更新。) 问题在于 readObject not 在到达 EOF 时是否返回 null;如果首先将 null 写入流,它只会返回 null。因此,您无法检查 null 以检测 EOF。为了避免这个问题,读取代码要么必须事先知道要读取多少个对象,必须以某种方式在序列化流中发送对象的数量,要么需要写入一个标记对象(或 null)来指示应该读取停下来。

    查看您的other question,编写代码如下所示:

    // open objectOutputStream on a file
    for (Student s : this.list) {
        objectOutputStream.writeObject(s);
    }
    

    大概listList<Student> 或类似的。可以这样修改编写代码:

    objectOutputStream.writeObject(list.size());
    for (Student s : this.list) {
        objectOutputStream.writeObject(s);
    }
    

    然后把阅读代码改成这样:

    int count = (int)objectInputStream.readObject();
    for (int i = 0; i < count; i++) {
        this.list.add((Student)objectInputStream.readObject());
    }
    

    这可能是不必要的,因为标准集合(例如ArrayList)本身是可序列化的,前提是它们的内容是可序列化的。由于列表“知道”它包含多少元素,因此您不必编写计数,只需序列化和反序列化整个列表即可:

    objectOutputStream.writeObject(list);
    
    ...
    
    @SuppressWarnings("unchecked")
    List<Student> list = (List<Student>)objectInputStream.readObject();
    

    请注意,在反序列化时您必须取消未检查的警告,因为实际上没有任何东西可以检查反序列化列表是否实际包含 Student 实例。

    【讨论】:

    • 格式很好,没有比这更好的答案了。非常感谢斯图尔特!
    • readObject() 如果您写了一个空值,则返回一个空值。这就是为什么 null 不能用作 EOS 指标的原因:t 不是带外值。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-19
    • 2014-09-09
    • 1970-01-01
    • 2011-05-17
    • 2011-10-01
    • 2012-12-27
    • 1970-01-01
    相关资源
    最近更新 更多