Juxhin Juxhin - 17 days ago 9
Java Question

Java - Serialization - EOFException Issue

Sorry for the second post today regarding serialization, fixing one issue caused another.




As states here - Java - Serialization - NotSerializableException Issue - I have a project with following classes


Student.java

StudentsCollection.java


Students creates my Student object(Self explanatory) and my StudentsCollection() instantiates a list of type Student which stores my Student objects, when trying to save/load the objects I use this code and get the following exception thrown:

/**
* 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");
}

}


And is throwing:

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)


I only have one Student object added to my list, after saving the list and re-opening it, I ask the console to print out that list, the Student actually prints out. However when I created multiple student objects and incremented their ID by 1 each time and added them, the console would print them ALL in order then re-print(for some odd reason) and end up skipping a few.

If you need any more code just ask. The saveCollection() is working fine now

Revised code:
/**

* 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");
}

}


Throws:

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)


Only have 1 student object and the following is printed in the console:


Student name: Student
Student surname: Default
Student ID: 0 Student
DoB: 1/1/90


And printed once again:


Student name: Student
Student surname: Default
Student ID: 0
Student DoB: 1/1/90

Answer

The problem is that your read loop doesn't know how many objects to read, so it runs off the end of the file and gets EOFException.

The reading code does this:

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

The problem is that readObject never returns null. (Updated per comment from EJP.) The problem is that readObject does not return null when it reaches EOF; it only returns null if null was written to the stream in the first place. Therefore, you can't check for null to detect EOF. To avoid this problem, the reading code either has to know in advance how many objects to read, the number of objects has to be sent in the serialized stream somehow, or a sentinel object (or null) needs to be written indicating that reading should stop.

From looking at your other question, the writing code looks something like this:

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

Presumably, list is a List<Student> or similar. One could modify the writing code thus:

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

and then change the reading code to this:

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

This is likely unnecessary, since the standard collections, such as ArrayList, are themselves serializable, provided that their contents are serializable. Since the list "knows" how many elements it contains, you don't have to write the count, and you can just serialize and deserialize the entire list:

objectOutputStream.writeObject(list);

...

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

Note that you have to suppress the unchecked warning when deserializing, since there is nothing that actually checks that the deserialized list actually contains Student instances.