6

I'm a student practicing my File IO skills and I am coming up against a problem with reading Objects from a file using ObjectInputStream. The code is consistently throwing an InvalidClassException and I can't find how the code is throwing it online or by trial and error. Here's my code:

import java.io.*;
import java.util.ArrayList;
import java.util.List;

public class ReadFromFile {
String filename;
List<Object> os;

public ReadFromFile(String filename) {
    this.filename = filename;
    os = new ArrayList<>();
}

public Object[] readObject() {
    try {
        FileInputStream fis = new FileInputStream(filename);
        ObjectInputStream ois = new ObjectInputStream(fis);
        System.out.print("reading\n");
        while (true) {
            os.add(ois.readObject());
            System.out.print("read one\n");
        }
    } catch (EOFException e) {
        return os.toArray();
    } catch (FileNotFoundException e) {
        System.out.print("File not found\n");
        return os.toArray();
    } catch (ClassNotFoundException e) {
        System.out.print("Class not found\n");
        return os.toArray();
    } catch (StreamCorruptedException e) {
        System.out.print("SC Exception\n");
        e.printStackTrace();
        return os.toArray();
    } catch (InvalidClassException e) {
        e.printStackTrace();
        System.out.print("IC Exception\n");
        return os.toArray();
    } catch (OptionalDataException e) {
        System.out.print("OD Exception\n");
        return os.toArray();
    } catch (IOException e) {
        System.out.print("IO Exception\n");
        return os.toArray();
    }
}
} 

I wrote all of the separate catch blocks to figure out what Exception was being thrown and it always throws the InvalidClassException.

Here also is my Tree Class:

import java.io.Serializable;

public class Tree implements Serializable {
private static final long serialVersionUID = -310842754445106856L;
String species;
int age;
double radius;

public Tree() {
    this.species = null;
    this.age = 0;
    this.radius = 0;
}
public Tree(String species, int age, double radius) {
    this.species = species;
    this.age = age;
    this.radius = radius;
}

public String toString() {
    return species + ", age: " + age + ", radius: " + radius;
}
}

And here is my write to file function:

public boolean write(Object object) {
    try {
        File f = new File(filename);
        FileOutputStream fos = new FileOutputStream(f,true);
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(object + "\n");
        oos.close();
    } catch (FileNotFoundException e) {
        System.out.print("File Not Found\n");
        return false;
    } catch (IOException e) {
        System.out.print("IOException\n");
        return false;
    }
    return true;
}

Your knowledge is appreciated...

Stack trace:

SC Exception
java.io.StreamCorruptedException: invalid stream header: 0AACED00
at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:806)
at java.io.ObjectInputStream.<init>(ObjectInputStream.java:299)
at ReadFromFile.readObject(ReadFromFile.java:17)
at WriteAndRecord.main(WriteAndRecord.java:21)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at    sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:497)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:140)

Process finished with exit code 0

user207421
  • 305,947
  • 44
  • 307
  • 483
Plewistopher
  • 73
  • 1
  • 1
  • 7
  • 1
    Also post a stacktrace please. That's the best source to start pinpointing the error. – Christophe De Troyer Nov 09 '15 at 23:35
  • 1
    What has the class `Tree` got to do with the source? – t0mm13b Nov 09 '15 at 23:37
  • 1
    Is that one program? Class Tree should be available in classpath to be able deserialize object to it. – vvg Nov 09 '15 at 23:39
  • 1
    To expand on @Mr.V.'s comment, `Tree` must be visible to *both* the serializer *and* the deserializer. "read one" is never printed, is it? – MeetTitan Nov 09 '15 at 23:43
  • I don't know what a stacktrace is, sorry. The 'Tree' Class could be part of the problem. These are all classes in a project and because the 'Tree' class is public it should be visible to anything, right? And, yes, "read one" is never printed. – Plewistopher Nov 09 '15 at 23:50
  • 1. You can't append to a stream created by `ObjectOutputStream`. 2. When you get an exception, print the stack trace. Not some message of your own devising. In this case `e.printStackTrace();` is sufficient. 3. `while ((o = ois.readObject()) != null )` isn't a valid loop. `readObject()` doesn't return `null` at end of stream: it throws `EOFException`. `null` can occur any time in the stream you wrote one. – user207421 Nov 10 '15 at 00:11
  • @Gruzz A StackTrace is basically just an error message. Also, can you let me know if my answer helps you? I have a hunch it will, but you never know. – Zizouz212 Nov 10 '15 at 00:26
  • @Gruzz Until you provide the stack trace this question is incomplete and unanswerable. – user207421 Nov 10 '15 at 00:53
  • @EJP Here's where the stack trace is at now: java.io.StreamCorruptedException: invalid stream header: 0AACED00 SC Exception at ... Process finished with exit code 0 It's too long for this comment area. – Plewistopher Nov 10 '15 at 00:55
  • Stack traces should be posted in the question. The reason for the invalid stream header is mentioned in my point (1) above. – user207421 Nov 10 '15 at 01:02
  • @EJP If I can't append using ObjectOutputStream then what is the point of having an ObjectOutputStream? – Plewistopher Nov 10 '15 at 01:07

2 Answers2

4
 java.io.StreamCorruptedException: invalid stream header: 0AACED00 

This is caused by appending to the FileOutputStream. As I mentioned in a comment above, you can't append to a stream written by ObjectOutputStream, at least not without special measures. Keep the file and the ObjectOutputStream open until you've written all the objects you want to write, then close it, then deserialize from it.

NB As I also mentioned,

while ((object = in.readObect()) != null)

is not a valid object-reading loop. readObject() doesn't return null at end of stream: it throws EOFException. null can occur anywhere in the stream, any time you write one. The correct form of the loop is:

try
{
    for (;;)
    {
        Object object = in.readObject();
        // ...
    }
}
catch (EOFException exc)
{
    // end of stream
}
// other catch blocks ...

NB 2 This:

oos.writeObject(object + "\n");

should be just

oos.writeObject(object);

Otherwise you're implicity calling toString() and pointlessly appending a line terminator, so the result of readObject() will be a String, not the original object.

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

I think this was caused by the lack of a serialVersionUID.

Whenever you serialize an object, the ClassLoader needs something to verify the new loaded object against to verify it and ensure its compatibility. In order to do this, you just need to define a field in your class like this:

private static final long serialVersionUID = 12358903454875L;

Your IDE may have also given you a warning stating the lack of it (Eclipse does this).

This should solve your problem.

You can learn more in this excellent answer by Jon Skeet here: What is a serialVersionUID and why should I use it?.

Zizouz212
  • 4,908
  • 5
  • 42
  • 66
  • It would be caused by the lack of a `serialVersionUID` *if* he had versioned the class. Not otherwise. – user207421 Nov 10 '15 at 00:15
  • @EJP Are you sure? [This](http://www.tutorialspoint.com/java/java_serialization.htm) article seems to support my theory, at least looking briefly at the OPs code, in that it looks like he's trying to deserialize an object. Of course, I don't know much about serialization (and I don't normally use it), but this may be the case. – Zizouz212 Nov 10 '15 at 00:20
  • Inserting that line of code into my 'Tree' class let it read one Object from the file, but now it throws a _StreamCorruptedException_. This may have to do with some of the things @EJP was saying. – Plewistopher Nov 10 '15 at 00:29
  • @Gruzz Alright, that helps. In that case, I would now expect the comment from EJP above on your question to now provide you with a solution :) – Zizouz212 Nov 10 '15 at 00:30
  • Arbitrary Internet junk such as your link doesn't have any status in this discussion. *My* reference is the [Object Serialization Specification](http://docs.oracle.com/javase/7/docs/platform/serialization/spec/class.html#4100), where it says "Each *versioned* class must identify the original class version for which it is capable of writing streams and from which it can read ... The value must be declared in all versions of a class *except the first*" [emphasis added]. But there is nothing in your link that supports your assertion. `serialVersionUID` isn't even mentioned. – user207421 Nov 10 '15 at 00:44
  • In any case a `serialVersionUID` problem would show up as an `InvalidClassException`, not a `StreamCorruptedException`. Answer is completely incorrect. – user207421 Apr 06 '17 at 23:20