Amit Petkar Amit Petkar - 1 month ago 15
Java Question

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.

Answer

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.