Why does java.io.EOFException appear in new ObjectInputStream

1. Example code

package com.softeem.wolf.homework06;

import java.io.*;

/**
 * Created by 苍狼
 * Time on 2023-05-24
 */
public class App {
    public static void main(String[] args) throws IOException {
        ObjectInputStream ois = null;
        ObjectOutputStream oos = null;
        ois = new ObjectInputStream(new FileInputStream("src\\com\\softeem\\wolf\\homework06\\student.txt"));
        oos = new ObjectOutputStream(new FileOutputStream("src\\com\\softeem\\wolf\\homework06\\student.txt"));
        
        Student student1 = new Student(1,"张三",'男');
        Student student2 = new Student(2, "李四",'女');

        try {
            oos.writeObject(student1);
            oos.writeObject(student2);
            oos.writeObject(null);
            oos.close();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
        try {
            Object object;
            while((object=ois.readObject())!=null){
                System.out.println(object);
            }
            ois.close();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        try {
            ois.close();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }
}

Create a student.txt file under the path of src\\com\\softeem\\wolf\\homework06\\ and run the program.

It is found that EOFException will occur. Why is this?

Second, analyze the reasons

We use the form of debugs to analyze the reasons step by step.

The exception information shows ois = new ObjectInputStream(new FileInputStream("src\\com\\softeem\\wolf\\homework06\\student.txt")); there is a problem,

Let's click in to see the constructor of ObjectInputStream

public ObjectInputStream(InputStream in) throws IOException {
        verifySubclass();
        bin = new BlockDataInputStream(in);
        handles = new HandleTable(10);
        vlist = new ValidationList();
        enableOverride = false;
        readStreamHeader();
        bin.setBlockDataMode(true);
    }

It is found that a readStreamHeader() method will be called here to get the header of the data stream in the file, and then we click in to see this readStreamHeader() method.

protected void readStreamHeader()
        throws IOException, StreamCorruptedException
    {
        short s0 = bin.readShort();
        short s1 = bin.readShort();
        if (s0 != STREAM_MAGIC || s1 != STREAM_VERSION) {
            throw new StreamCorruptedException(
                String.format("invalid stream header: %04X%04X", s0, s1));
        }
    }

This method calls bin.readShort(), see the source code of the readShort() method.

public short readShort() throws IOException {
            if (!blkmode) {
                pos = 0;
                in.readFully(buf, 0, 2);
            } else if (end - pos < 2) {
                return din.readShort();
            }
            short v = Bits.getShort(buf, pos);
            pos += 2;
            return v;
        }

It is found that it will call the method in.readFully(buf, 0, 2), and then go to the source code of this method to have a look.

void readFully(byte[] b, int off, int len) throws IOException {
            int n = 0;
            while (n < len) {
                int count = read(b, off + n, len - n);
                if (count < 0) {
                    throw new EOFException();
                }
                n += count;
            }
        }

Then call read(b, off+n, len-n) in this method, let's take a look at the source code.

public int read(byte[] b, int off, int len) throws IOException {
            if (len == 0) {
                return 0;
            } else if (peekb < 0) {
                return in.read(b, off, len);
            } else {
                b[off++] = (byte) peekb;
                len--;
                peekb = -1;
                int n = in.read(b, off, len);
                return (n >= 0) ? (n + 1) : 1;
            }
        }

It will run until return in.read(b, off, len); Let's click in to see the source code.

public int read(byte b[], int off, int len) throws IOException {
        if (b == null) {
            throw new NullPointerException();
        } else if (off < 0 || len < 0 || len > b.length - off) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int c = read();
        if (c == -1) {
            return -1;
        }
        b[off] = (byte)c;

        int i = 1;
        try {
            for (; i < len ; i++) {
                c = read();
                if (c == -1) {
                    break;
                }
                b[off + i] = (byte)c;
            }
        } catch (IOException ee) {
        }
        return i;
    }

The final result returned by this method is -1; then the result is uploaded all the way to the readFully(byte[] b, int off, int len) method for judgment. If count<0, an EOFException is thrown. That's the whole process of new ObjectInputStream() object.

The readStreamHeader() method in the ObjectInputStream constructor is to obtain the stream of the file header, but because our student.txt file is empty, the file header stream cannot be obtained, resulting in a count of -1, resulting in an EOFException exception.

Three, the solution

The solution is also very simple. Just swap the order of new ObjectOutputStream() and new ObjectInputStream().

package com.softeem.wolf.homework06;

import java.io.*;

/**
 * Created by 苍狼
 * Time on 2023-05-24
 */
public class App {
    public static void main(String[] args) throws IOException {
        ObjectInputStream ois = null;
        ObjectOutputStream oos = null;
        oos = new ObjectOutputStream(new FileOutputStream("src\\com\\softeem\\wolf\\homework06\\student.txt"));
        ois = new ObjectInputStream(new FileInputStream("src\\com\\softeem\\wolf\\homework06\\student.txt"));
        Student student1 = new Student(1,"张三",'男');
        Student student2 = new Student(2, "李四",'女');

        try {
            oos.writeObject(student1);
            oos.writeObject(student2);
            oos.writeObject(null);
            oos.close();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
        try {
            Object object;
            while((object=ois.readObject())!=null){
                System.out.println(object);
            }
            ois.close();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        try {
            ois.close();
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }
}

operation result

Because oos = new ObjectOutputStream(new FileOutputStream("src\\com\\softeem\\wolf\\homework06\\student.txt")) at runtime, in the constructor

The writeStreamHeader() method will write the file header, which can prevent the ObjectInputStream() constructor from detecting the file header when running the readStreamHeader() method.

The following is the source code of the constructor in ObjectOutputStream.

public ObjectOutputStream(OutputStream out) throws IOException {
        verifySubclass();
        bout = new BlockDataOutputStream(out);
        handles = new HandleTable(10, (float) 3.00);
        subs = new ReplaceTable(10, (float) 3.00);
        enableOverride = false;
        writeStreamHeader();
        bout.setBlockDataMode(true);
        if (extendedDebugInfo) {
            debugInfoStack = new DebugTraceInfoStack();
        } else {
            debugInfoStack = null;
        }
    }

Guess you like

Origin blog.csdn.net/m0_50370837/article/details/130935554