zoukankan      html  css  js  c++  java
  • EOFException异常详解

    最近线上的系统被检测出有错误日志,领导让我检查下问题,我就顺便了解了下这个异常。

      了解一个类,当然是先去看他的API,EOFException的API如下:

      通过这个API,我们可以得出以下信息:

    • 这是一个IO异常的子类,名字也是END OF FILE的缩写,当然也表示流的末尾
    • 它在表明一个信息,流已经到末尾了,而大部分做法是以特殊值的形式返回给我们,而不是抛异常

      也就是说这个异常是被主动抛出来的,而不是底层或者编译器返回给我的,就像NullPointerException或IndexOutOfBoundsException一样。

      我们先来看InputStream,这个输入流,当读到了结尾会怎么样,看看API介绍:

      可以看到如果到达流的末尾,那么会返回-1,也就是说我们可以根据这个-1来判断是否到达流的末尾。

      同样的我们看一下输入流的包装类BufferedReader,它有一个读一行的方法:

     

      也可以发现当读到流的末尾,通过返回值null来告诉我们到达流的末尾了,也就是说通过返回一个不可能的值来表示到达流的末尾。

      那我们找一个EOFException的例子,在jdk类中就有一个,那就是ObjectInputStream,我写了一个测试代码,如下:

    复制代码
    package yiwangzhibujian.objectstream;
    
    import java.io.ByteArrayInputStream;
    import java.io.ByteArrayOutputStream;
    import java.io.ObjectInputStream;
    import java.io.ObjectOutputStream;
    import java.io.Serializable;
    
    public class ObjectStream {
      public static void main(String[] args) throws Exception {
        User user1=new User("yiwangzhibujian",27);
        User user2=new User("laizhezhikezhui",24);
        
        ByteArrayOutputStream bos=new ByteArrayOutputStream();
        ObjectOutputStream oos=new ObjectOutputStream(bos);
        
        oos.writeObject(user1);
        oos.writeObject(user2);
        oos.writeObject(null);
        
        byte[] data = bos.toByteArray();
        ByteArrayInputStream bis=new ByteArrayInputStream(data);
        ObjectInputStream ois=new ObjectInputStream(bis);
        
        System.out.println(ois.readObject());
        System.out.println(ois.readObject());
        System.out.println(ois.readObject());
        System.out.println(ois.readObject());
      }
    }
    class User implements Serializable{
      private static final long serialVersionUID = 1L;
      public String name;
      public int age;
      public User(String name, int age) {
        this.name = name;
        this.age = age;
      }
      @Override
      public String toString() {
        return "User [name=" + name + ", age=" + age + "]";
      }
    }
    复制代码

      控制台输出结果为:

    复制代码
    User [name=yiwangzhibujian, age=27]
    User [name=laizhezhikezhui, age=24]
    null
    Exception in thread "main" java.io.EOFException
        at java.io.ObjectInputStream$BlockDataInputStream.peekByte(ObjectInputStream.java:2608)
        at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1319)
        at java.io.ObjectInputStream.readObject(ObjectInputStream.java:371)
        at yiwangzhibujian.objectstream.ObjectStream.main(ObjectStream.java:28)
    复制代码

      可以感觉到EOFException的用意,因为我们可以往流中放入null值,所以我们没法找到一个不可能的值来表示到达流的末尾,所以只能通过抛异常的方式来告诉用户到达末尾了,相应的抛异常部分的源码如下:

    复制代码
    byte peekByte() throws IOException {
         int val = peek();
         if (val < 0) {
              throw new EOFException();
         }
         return (byte) val;
    }
    复制代码

      也就是说,ObjectInputStream在读取具体的对象之前会优先读取一个标识符,它通过是否能读到符号来判断是否到达流的末尾,因为再底层的流会通过返回-1来表明,然后ObjectInputStream会根据标识符来判断读到的是什么类型,因为ObjectOutputStream 在写入内容的时候会这么做:

      所以说ObjectInputStream可以自己判断流是否到达末尾,但是它无法告诉我们,我们不能替代他们读取这个标记,不然ObjectInputStream将识别不了下一个内容的实际类型。

      所以呢,对于这种异常的一般解决方法就是,捕获,可以记录日志,也可以不做处理,捕获异常以后,把之前读到的数据进行后续的处理就可以了,因为那就是所以的数据。还有就是如果打算记录日志,不要把它的堆栈信息打印出来,容易给人以错觉。毕竟EOFException实质上只是一个消息而已。

      当然抛异常的做法还是有一些偏激,但是当ObjectInputStream在不知道读取对象数量的情况下,确实无法判断是否读完,除非你把之前写入对象流的数量记录下来。所以说出现这个异常时就认真分析一下,这个异常是不是代表一个信息。

      希望我对这个问题的理解,能帮助到遇到同样问题的人。

    需求:  *1、创建54张扑克牌,将扑克牌写入文件card.txt  *2、将写入的文件内容,读取出来,可以生成相对应的54张扑克牌  *3、保证扑克牌可以调用自己的方法 

     遇到的问题以及解决方法:

    1.序列化的问题:你要创建的对象在流中传输,必须将此类对象进行序列化,就是implements Serializable接口

    2.EOFException的问题: 你从文件中读取对象的时候,如何判断是否读取完毕。jvm会给抛出EOFException,表示的是,文件中对象读取完毕。所以呢,你在判断是否读取结束的时候,捕获掉这个异常就可以,是捕获不是抛出。

    重要的说三次,是捕获,捕获,捕获!

    代码如下:

    1.  
      package day02;
    2.  
       
    3.  
      import java.io.EOFException;
    4.  
      import java.io.File;
    5.  
      import java.io.FileInputStream;
    6.  
      import java.io.FileNotFoundException;
    7.  
      import java.io.FileOutputStream;
    8.  
      import java.io.IOException;
    9.  
      import java.io.ObjectInputStream;
    10.  
      import java.io.ObjectOutputStream;
    11.  
      import java.util.ArrayList;
    12.  
      import java.util.List;
    13.  
       
    14.  
      /**
    15.  
      *1、创建54张扑克牌,将扑克牌写入文件card.txt
    16.  
      *2、将写入的文件内容,读取出来,可以生成相对应的54张扑克牌
    17.  
      *3、保证扑克牌可以调用自己的方法
    18.  
      */
    19.  
      public class Exercis {
    20.  
      public static void main(String[] args) throws FileNotFoundException, IOException {
    21.  
      ObjectOutputStream os=new ObjectOutputStream(new FileOutputStream(new File("./card.txt")));
    22.  
      List<Card> lists=new ArrayList<Card>();
    23.  
      for(int i=Card.THREE;i<=Card.TWO;i++){
    24.  
      lists.add(new Card(Card.HEITAO,i));
    25.  
      lists.add(new Card(Card.HONGTAO,i));
    26.  
      lists.add(new Card(Card.MEIHUA,i));
    27.  
      lists.add(new Card(Card.FANGKUAI,i));
    28.  
       
    29.  
      }
    30.  
      lists.add(new Card(Card.JOKER,Card.BLACK));
    31.  
      lists.add(new Card(Card.JOKER,Card.COLOR));
    32.  
      for(Card c : lists){
    33.  
      os.writeObject(c);
    34.  
      }
    35.  
      ObjectInputStream is=new ObjectInputStream(new FileInputStream(new File("./card.txt")));
    36.  
      while(true){
    37.  
      Object o = null;
    38.  
      try {
    39.  
      o = is.readObject();
    40.  
      if(o instanceof Card){
    41.  
      System.out.println(o);
    42.  
      }
    43.  
      } catch (ClassNotFoundException e) {
    44.  
      // TODO Auto-generated catch block
    45.  
      e.printStackTrace();
    46.  
      }catch(EOFException e){
    47.  
      System.out.println("读写完毕!");
    48.  
      os.close();
    49.  
      is.close();
    50.  
      break;
    51.  
      }
    52.  
      }
    53.  
      }
    54.  
      }
  • 相关阅读:
    Vue.js_础学习之DOM操作
    node REPL
    node npm
    Vue.js_getter and setter
    tomcat+nginx+redis实现均衡负载以及session共享
    深入浅出微服务框架dubbo(一):基础篇
    Linux下安装zip解压功能
    Linux下查看CPU型号,内存大小,硬盘空间的命令
    Linux查看系统信息命令
    MyBatis自动生成代码之generatorConfig配置文件及其详细解读
  • 原文地址:https://www.cnblogs.com/firstdream/p/9591126.html
Copyright © 2011-2022 走看看