Amit Petkar
Amit Petkar

Reputation: 73

Java Object Serialization nested objects

I was studying Serialization in Java when I came across saving the state of objects which are not serializable and are referenced in Class(instance variables) to be serialized. In the following code, I am having class Dog (Serializable) which has reference to class Collar(not serializable); which in turn has reference to class Color (not serializable). I am getting error despite trying all the possibilities. This is the latest code I came up with:

class Color {
    private String colorName;

    public String getColorName() {
        return colorName;
    }

    public void setColorName(String colorName) {
        this.colorName = colorName;
    }

    Color(String color) {
        this.colorName = color;
    }
}

class Collar {

    private Color color;
    private int size;

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public Color getColor() {
        return color;
    }

    public void setColor(Color color) {
        this.color = color;
    }

    Collar(int size, Color color) {
        this.size = size;
        this.color = color;
    }
}

class Dog implements Serializable {

    Dog(String breed, Collar collar) {
        this.breed = breed;
        this.collar = collar;
    }
    private String breed;

    public String getBreed() {
        return breed;
    }

    public void setBreed(String breed) {
        this.breed = breed;
    }

    public Collar getCollar() {
        return collar;
    }

    public void setCollar(Collar collar) {
        this.collar = collar;
    }
    transient private Collar collar;

    private void writeObject(ObjectOutputStream os) {
        try {
            os.defaultWriteObject();
            os.writeInt(this.getCollar().getSize());
            os.writeUTF(this.getCollar().getColor().getColorName());
            os.close();
        } catch (IOException ex) {
            Logger.getLogger(Dog.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    private void readObject(ObjectInputStream is) {
        try {
            is.defaultReadObject();
            int size = is.readInt();
            String colorName = is.readUTF();
            this.setCollar(new Collar(size, new Color(colorName)));
            is.close();
        } catch (Exception ex) {
            Logger.getLogger(Dog.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

public class App0001 {

    public static void main(String[] args) {

        try {
            Dog d = new Dog("Great Dane", new Collar(3, new Color("RED")));
            //System.out.println(d.getCollar().getColor().getColorName());
            ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("obj.ser"));
            os.writeObject(d);
            os.close();

            ObjectInputStream is = new ObjectInputStream(new FileInputStream("obj.ser"));
            d = (Dog) is.readObject();
            System.out.println(d.getCollar().getColor().getColorName());
        } catch (Exception ex) {
            ex.printStackTrace();
        }

    }
}

And I am getting following error:

java.io.IOException: Write error
at java.io.FileOutputStream.writeBytes(Native Method)
at java.io.FileOutputStream.write(FileOutputStream.java:260)
at java.io.ObjectOutputStream$BlockDataOutputStream.drain(ObjectOutputStream.java:1847)
at java.io.ObjectOutputStream$BlockDataOutputStream.setBlockDataMode(ObjectOutputStream.java:1756)
at java.io.ObjectOutputStream.writeNonProxyDesc(ObjectOutputStream.java:1257)
at java.io.ObjectOutputStream.writeClassDesc(ObjectOutputStream.java:1211)
at java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1395)
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1158)
at java.io.ObjectOutputStream.writeFatalException(ObjectOutputStream.java:1547)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:333)
at Serialization.App0001.main(App0001.java:121)

This is not a production code. This is just for practice and understanding.

Upvotes: 5

Views: 7163

Answers (2)

isnot2bad
isnot2bad

Reputation: 24454

You must not close the streams in readObject and writeObject! If you do so, the next write/read attempt fails.

Usually, streams (as other resources) should be treated as follows:

  • If your method owns the stream, i.e. your method opened it - close it in the same method (usually this is done in a try-with-resource statement).
  • If your method does NOT own the stream, i.e. it got the stream passed from somewhere else (usually passed via method parameter), don't close it as you don't know what the owner of the stream wants to do with it after your method returns.

Upvotes: 2

Seelenvirtuose
Seelenvirtuose

Reputation: 20658

When writing to a stream, the "Write error" in an IOException occurs, if the stream is closed.

Analyzing your code, I see that you have a custom writeObject method in your class Dog. In that you must not close the stream, as it is needed for continued writing. So just remove the line

os.close();

in your writeObject method. Oh, and also remove the line

is.close();

in the readObject method.


Ok, I will explain it a bit more. You have the following code in your main method:

ObjectOutputStream os = new ObjectOutputStream(new FileOutputStream("obj.ser"));
os.writeObject(d);
os.close();

Here you are creating the stream, using it, and afterwards you close it. This is the correct place to close it, as this is the responsible place for the stream.

Imagine, you have a nested structure of serializable objects, whose class definitions all contain a custom writeObject method. When calling the writeObject method of an ObjectOutputStream, it walks through the object graph with calling the writeObject method of each object. The ObjectOutputStream is controlling the write order, and it also write control bytes itself. Creating and closing must be done outside (as you already did).

Upvotes: 1

Related Questions