Can't Serialize a class that's already serialized?

Look at the bottom below

    USPresident usPresident = new USPresident(56);
    try (ObjectOutputStream oos = new ObjectOutputStream(new
    FileOutputStream("/home/nikhil/dev/USPresident.data"))){
    oos.writeObject(usPresident);
    usPresident.setTerm(57);
    oos.writeObject(usPresident);
    System.out.println("Serialized");
    }

      

Created an instance of President with term 56. Serialized it. Reset the time limit to 57 Re-serialized it

But, when I deserialize the object, it still has this term as 56 (not 57!)

I see this explanation given by me book,

Yes, it will print 56 even if you changed the term using its setter to 57 and serialize again. This is due to the serialVersionUID, which is checked by the JVM during serialization. If the class has already been serialized and you try to serialize it again, the JVM will not serialize it.

But in my opinion the serialVersionUID is used to check if a class object matches a serialized object during deserialization. In addition, serialVersionUID is used as an identifier for the signature of the object, not the state.

I don't understand what's going on here. Can anyone explain this behavior?

Looked at the ObjectOutputStream implementation and this is how writeObjet is implemented

public final void writeObject(Object obj) throws IOException {
    if (enableOverride) {
        writeObjectOverride(obj);
        return;
    }
    try {
        writeObject0(obj, false);
    } catch (IOException ex) {
        if (depth == 0) {
            writeFatalException(ex);
        }
        throw ex;
    }
}

      

see boolean enableOverride. This must be true in order to write a "modified" object. However, this can only be set to true if using a subclass of ObjectOutputStream. See Protected Constructor below.

protected ObjectOutputStream() throws IOException, SecurityException {
    SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
    }
    bout = null;
    handles = null;
    subs = null;
    enableOverride = true;
    debugInfoStack = null;
}

      

Why is this strange behavior?

+3


source to share


3 answers


Your book is wrong as well serialVersionUID

, which is static, designed to work with different versions of the class itself.

The idea behind serialization is to take a living object (the one it points to usPresident

) and store all of its state in some external form (bytes, XML, JSON). You can then revert this state to a copy of the object. It's like saving a file in a text editor.



However, if you change the object after serializing it, there is no connection. You've already written the bytes to disk and they won't change because you are calling methods on the object. When you recreate an object from these bytes, it will have the same values ​​as when you saved it. If you forgot to save your changes to your text document, the file on disk still has the old content.

In your case, you are running into a quirk in Java serialization because you are repeating the same object multiple times in the same one ObjectOutputStream

. So that you can serialize complex object relationships, Java serialization only stores the object once and then binds to it when it is saved again. Close and reopen the stream and you will see the updated values.

+6


source


Why is this strange behavior?

This is the persistence of the object id

Put the same object in the collection three times, serialize it, deserialize it, and you still get the same object three times (not just three different objects with equivalent state). Without it, you would also not be able to serialize the self-referencing object graph (it will run in an infinite loop).

In addition, serialVersionUID is used as an identifier for the signature of the object, not the state.

It is right. Your book is wrong in this respect. serialVersionUID

has nothing to do with what's going on here.



But what confuses me is the second time "writeObject" is called. I expect it to overwrite the data written to disk.

This cannot be done in an efficient way.

You cannot get back into the stream (maybe already sent over the network, for example), and the serializer has no way of determining if the object has changed (and in doing so will unconditionally kill the ability to work with a circular graphics object again).

FWIW, while you cannot overwrite the first object, you can force the thread to write a new copy a second time by calling oos.reset()

before writing. Note that this, however, will write a second instance of all the objects you have, including things like strings.

+3


source


To overwrite an object (write a second copy of the object) with the updated state to a stream, you will need a reset()

stream to remove the copy of the object from the "known objects" set. This can be done as follows:

...
usPresident.setTerm(57);
oos.reset();  // <-- this guy!
oos.writeObject(usPresident);            

      

Also, when you read objects back - you will need to read two objects, the first one was saved (with state term == 56

) and the second (with state of term == 57

):

ObjectInputStream in = new ObjectInputStream(
     new FileInputStream("/home/nikhil/dev/USPresident.data"));
usPresident = (USPresident)in.readObject();
System.out.println("usPresident.getTerm() = " +usPresident.getTerm()); // prints 56

usPresident = (USPresident)in.readObject();
System.out.println("usPresident.getTerm() = " +usPresident.getTerm()); // prints 57

      

+2


source







All Articles