【疑難雜症04】EOFException異常詳解

  最近線上的系統被檢測出有錯誤日誌,領導讓我檢查下問題,我就順便了解了下這個異常。java

  瞭解一個類,固然是先去看他的API,EOFException的API以下:ide

  經過這個API,咱們能夠得出如下信息:測試

  • 這是一個IO異常的子類,名字也是END OF FILE的縮寫,固然也表示流的末尾
  • 它在代表一個信息,流已經到末尾了,而大部分作法是以特殊值的形式返回給咱們,而不是拋異常

  也就是說這個異常是被主動拋出來的,而不是底層或者編譯器返回給個人,就像NullPointerException或IndexOutOfBoundsException同樣。ui

  咱們先來看InputStream,這個輸入流,當讀到告終尾會怎麼樣,看看API介紹:this

  能夠看到若是到達流的末尾,那麼會返回-1,也就是說咱們能夠根據這個-1來判斷是否到達流的末尾。spa

  一樣的咱們看一下輸入流的包裝類BufferedReader,它有一個讀一行的方法:日誌

 

  也能夠發現當讀到流的末尾,經過返回值null來告訴咱們到達流的末尾了,也就是說經過返回一個不可能的值來表示到達流的末尾。code

  那咱們找一個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 + "]";
  }
}

  控制檯輸出結果爲:blog

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在不知道讀取對象數量的狀況下,確實沒法判斷是否讀完,除非你把以前寫入對象流的數量記錄下來。因此說出現這個異常時就認真分析一下,這個異常是否是表明一個信息。

 

  但願我對這個問題的理解,能幫助到遇到一樣問題的人。

相關文章
相關標籤/搜索