When do I use Objectoutputstream the parent class attributes are also saved?

Asked

Viewed 437 times

4

When I use the ObjectOutpuStream to write to file, parent class attributes will also be saved?

2 answers

3

When it comes to inheritance, there are some nuances as to what will or will not be included in serialization.

The ObjectOutputStream will serialize all hierarchy classes that are marked with java.io.Serializable and their descendants. Of these, the non-static, non-stransient attributes that are also marked with the said interface will be serialized.

Kind of tricky, huh? Let’s see one...

Practical example (with error)

First, two classes that will be referenced, one serializable and one not:

class OutraClasseSerializavel implements Serializable {
    int outroValorSerializavel;
}

class OutraClasse {
    int outroValor;
}

Second, a class "father" and a "daughter":

class Pai {
    OutraClasse outraClassePai;
    OutraClasseSerializavel outraClasseSerializavelPai;
    int valorPai;
}

class Filha extends Pai implements Serializable {
    OutraClasse outraClasseFilha;
    OutraClasseSerializavel outraClasseSerializavelFilha;        
    int valorFilha;
}

Note that the two classes have values and references for serializable and non-serializable classes.

What happens if we try to serialize the class Filha? There occurs a java.io.NotSerializableException because of the reference to the class which cannot be serialized OutraClasse in class Filha.

Practical example

If we remove the reference to the unserviceable class from the class Filha, the error does not occur:

class Filha extends Pai implements Serializable {
    OutraClasseSerializavel outraClasseSerializavelFilha;        
    int valorFilha;
}

Testing and analyzing the result

Let’s take a test:

Filha filha = new Filha();

//valores da classe filha
filha.valorFilha = 11;
filha.outraClasseSerializavelFilha = new OutraClasseSerializavel();
filha.outraClasseSerializavelFilha.outroValorSerializavel = 33;

//valores da classe pai
filha.valorPai = 22;
filha.outraClasseSerializavelPai = new OutraClasseSerializavel();
filha.outraClasseSerializavelPai.outroValorSerializavel = 44;
filha.outraClassePai = new OutraClasse();
filha.outraClassePai.outroValor = 55;

//serializa
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("filha.out")));
oos.writeObject(filha);
oos.close();

//recupera classe serializada
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("filha.out")));
Filha filhaRecuperada = (Filha) ois.readObject();
ois.close();

Finally, let’s print and analyze the returned values...

Primitive attribute in serializable class

System.out.println(filhaRecuperada.valorFilha);

Exit:

11

Obviously, the attribute of valorFilha is properly serialized and retrieved because it is part of the serializable class and is a primitive type.

Reference to the class serializable in a class also serializable

System.out.println(filhaRecuperada.outraClasseSerializavelFilha.outroValorSerializavel);

Exit:

33

The attribute outraClasseSerializavelFilha was also serialized correctly, as well as its value, because it is a reference to a class serialized from the class Filha which is serializable.

Primitive attribute in class Pai, that is not serializable

System.out.println(filhaRecuperada.valorPai);

Exit:

0

We now note that, although no errors occur, static attributes in a nonserializable superclass are not serialized.

Reference to serializable and non-serializable classes in a non-proserializable superclass

System.out.println(filhaRecuperada.outraClassePai);
System.out.println(filhaRecuperada.outraClasseSerializavelPai);

Exit:

null

null

And finally, we observe that references to classes of any kind (serializable or not) in a non-savizable superclass will also be excluded from serialization.

Considerations

Extending a class to make it serializable does not work, because as it was seen the serialization process ignores the non-proserializable superclasses and an error occurs when including a non-serializable attribute.

But is there a solution? The answer is yes!

Solution: readObject and writeObject

To class documentation java.io.Serializable points out some methods that should be implemented so that you can change "manually" the way Java serializes and deserializes an object.

The signatures are:

private void writeObject(java.io.ObjectOutputStream out)
    throws IOException
private void readObject(java.io.ObjectInputStream in)
    throws IOException, ClassNotFoundException;

Example of implementation

Follows a basic implementation of methods readObject() and writeObject() in the Daughter class that solve the problem of serialization of both the superclass integer attribute and references to other objects:

class Filha extends Pai implements Serializable {
    
    int valorFilha;
    transient OutraClasse outraClasseFilha;
    OutraClasseSerializavel outraClasseSerializavelFilha;
    
    private void readObject(java.io.ObjectInputStream stream)
            throws IOException, ClassNotFoundException {
        valorFilha =  stream.readInt();
        outraClasseFilha = new OutraClasse();
        outraClasseFilha.outroValor = stream.readInt();
        outraClasseSerializavelFilha = (OutraClasseSerializavel) stream.readObject();
        
        valorPai = stream.readInt();
        outraClassePai = new OutraClasse();
        outraClassePai.outroValor = stream.readInt();
        outraClasseSerializavelPai = (OutraClasseSerializavel) stream.readObject();
    }

    private void writeObject(java.io.ObjectOutputStream stream)
            throws IOException {
        stream.writeInt(valorFilha);
        stream.writeInt(outraClasseFilha.outroValor);
        stream.writeObject(outraClasseSerializavelFilha);
        
        stream.writeInt(valorPai);
        stream.writeInt(outraClassePai.outroValor);
        stream.writeObject(outraClasseSerializavelPai);
    }
    
}

Then we make a new test:

Filha filha = new Filha();

//valores da classe filha
filha.valorFilha = 11;
filha.outraClasseSerializavelFilha = new OutraClasseSerializavel();
filha.outraClasseSerializavelFilha.outroValorSerializavel = 22;
filha.outraClasseFilha = new OutraClasse();
filha.outraClasseFilha.outroValor = 33;

//valores da classe pai
filha.valorPai = 44;
filha.outraClasseSerializavelPai = new OutraClasseSerializavel();
filha.outraClasseSerializavelPai.outroValorSerializavel = 55;
filha.outraClassePai = new OutraClasse();
filha.outraClassePai.outroValor = 66;

//serializa
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("c.out")));
oos.writeObject(filha);
oos.close();

//recupera classe serializada
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(new File("c.out")));
Filha filhaRecuperada = (Filha) ois.readObject();
ois.close();

//valores da classe filha
System.out.println(filhaRecuperada.valorFilha);
System.out.println(filhaRecuperada.outraClasseSerializavelFilha.outroValorSerializavel);
System.out.println(filhaRecuperada.outraClasseFilha.outroValor);

//valores da classe pai
System.out.println(filhaRecuperada.valorPai);
System.out.println(filhaRecuperada.outraClasseSerializavelPai.outroValorSerializavel);
System.out.println(filhaRecuperada.outraClassePai.outroValor);

And we get the exit:

11
22
33
44
55
66

All attributes have been saved!

Completion

Although Java does not solve the whole issue of serialization automagically, it provides us with a practical and flexible mechanism to resolve this, as it allows you to completely control how the object is saved and recovered from the file.

On the other hand, manual coding of each class attribute is required in the correct order.

1


Yes, not only the attributes of the object itself are saved, but also those of the superclasses, and also all objects referenced. That is, a sufficient "graph" of objects is recorded to restore the saved object completely, without relying on any pre-existing object.

Example:

class A {
    int x;
}

class B {
    A y;
}

class C extends B {
    int z;
}

By serializing an object of C, the fields will be included y and z, whereas y will be a serialization of the class object A (that is to say, x will also be included).

Note: this example is simplified. According to the documentation of ObjectOutputStream, only objects implementing the interface Serializable will be written in the archive. If memory serves me correctly, any nonserializable object in the object graph will cause an exception to be thrown when attempting serialization - unless there is code of its own to deal with this case.

  • 1

    I think it’s prudent to quote that they’re not all the referenced objects that are serialized -- Fields marked with transient are not serialized.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.