2

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

Community
  • 1
  • 1
Juxhin
  • 5,068
  • 8
  • 29
  • 55

3 Answers3

3

readObject() doesn't return null at end of stream, so testing for it as your loop termination condition is invalid. Your code is correctly throwing EOFException when it encounters end of stream. There is no problem here to solve.

user207421
  • 305,947
  • 44
  • 307
  • 483
0

You're not closing the input stream. My guess is you're not closing the output stream, and it's getting truncated. Use "try-with-resource".

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

becomes

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

(Edited to flush the wrapper stream (you don't have to go all the way and close it, but it's probably best rather than risk making a common programming error).)

That or you haven't written the terminating null.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
  • What terminating `null`? – user207421 Nov 22 '16 at 01:04
  • @EJP The one read with `while(inputSteam.readObject() != null){`. Mind you, it's an odd scheme that writes some throw away object to mark non-termination. – Tom Hawtin - tackline Nov 22 '16 at 08:05
  • He is not writing a terminating null. Ergo there is no terminating null. I don't understand your final sentence. – user207421 Mar 30 '17 at 01:17
  • @EJP At the time I answered the question the write code had not been added. A reasonable serialisation scheme (can't recall whether it is used within the Java library or not) is to write a `null`. As the read code was attempting to check for a `null` a reasonable suspicion is that a `null` was intended, but not written for one reason or another. – Tom Hawtin - tackline Mar 30 '17 at 02:28
  • Now you're contradicting yourself. A scheme can't be both 'odd' and 'reasonable' at the same time. Your 'reasonable' scheme prevents you from writing a null at any other time. The null is redundant given that `EOFException` is already thrown. – user207421 Sep 20 '17 at 23:08
  • @EJP I guess I have a more tolerant approach to what I consider reasonable. / I wouldn't want to substitute catching `EOFException` in place of a null check. The exception may be thrown (I think) from erroneous custom serialisation (or a truncated file). Together with the generally accepted Java custom of checking ahead of time instead of catching. – Tom Hawtin - tackline Sep 23 '17 at 10:08
  • You *have* to substitute catching `EOFException` for a null check. That's how the API behaves. You can't change it. There is no such 'generally accepted Java custom', and any tendency to the contrary is erroneous. – user207421 Jun 03 '22 at 08:17
0

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.

Community
  • 1
  • 1
Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
  • Very well formatted, couldn't have asked for a better answer. Thanks alot Stuart! – Juxhin Jul 21 '14 at 09:05
  • readObject() returns a null if you wrote a null. That's why null can't be used as an EOS indicator: t's not an out of band value. – user207421 Jul 22 '14 at 23:59