When objects are being serialized using the writeObject()
method, if the same each individual object is encountered more than once, it is written to the output stream only once, and after the first occurrence, only a reference . Calls to the writeObject()
method for the second and subsequent occurrences, place references to the first occurrence is written to in the stream. Correspondingly, the readObject()
method resolves produces at most one actual object for each object found in the input stream; references written by writeObject()
become references to multiple occurrences of the same first object found in the stream.
According to the Java API [API 2011], the writeUnshared()
method:
...
Reads an "unshared" object from the ObjectInputStream. This method is identical to readObject, except that it prevents subsequent calls to readObject and readUnshared from returning additional references to the deserialized instance obtained via this call.
This means that the writeUnshared()
/readUnshared()
methods cannot be used for round-trip serialization of data structures that require a one-to-one mapping between objects pre-serialization and objects post-deserialization. One common example of such a data structure is a graph or network of objects that may contain reference cycles.
This means that to serialize a network of objects containing circular references and then to successfully deserialize the same network the writeUnshared()
/readUnshared()
methods must not be used.
Consider the following code example.
...
Professor
and Students
are types that extend the basic type Person
. A student (i.e., an object of type Student
) has a tutor of type Professor
. A professor (i.e., an object of type Professor
) has a list (actually, an ArrayList
) of tutees (of type Student
). The method checkTutees()
checks whether all of the tutees of this professor have this professor as their tutor, returning true
if that is the case and false
otherwise. We then create Professor Jane who has three tutees, Able, Baker, and Charlie, all of whom have Professor Jane as their tutor. The println()
statement prints true
.
Noncompliant Code Example
This noncompliant code example attempts to serialize the data from the example above using writeUnshared()
. However, when the data is deserialized using readUnshared()
, the checkTutees()
method no longer returns true
because the tutor objects of the three students are different objects from the original Professor
object.
Code Block | ||
---|---|---|
| ||
String filename = "serial"; try { System.out.println("Serializing using writeUnshared"); ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream(filename)); oos.writeUnshared(jane); oos.close(); System.out.println("Deserializing using readUnshared"); ObjectInputStream ois = new ObjectInputStream (new FileInputStream(filename)); Professor jane2 = (Professor)ois.readUnshared(); ois.close(); System.out.println("checkTutees returns: " + jane3.checkTutees()); // prints "checkTutees returns: false" } catch(Exception e) { System.out.println("Exception during deserialization" + e); } |
Compliant Solution
This compliant solution overcomes the problem of the noncompliant code example by using writeObject()
and readObject()
, ensuring that the tutor objects of object referred to by the three students are the same as has a one-to-one mapping with the original Professor
object.
Code Block | ||
---|---|---|
| ||
String filename = "serial"; try { System.out.println("Serializing using writeObject"); ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream(filename)); oos.writeObject(jane); oos.close(); System.out.println("Deserializing using readObject"); ObjectInputStream ois = new ObjectInputStream (new FileInputStream(filename)); Professor jane2 = (Professor)ois.readObject(); ois.close(); System.out.println("checkTutees returns: " + jane2.checkTutees()); // prints "checkTutees returns: true" } catch(Exception e) { System.out.println("Exception during deserialization" + e); } |
Applicability
Using the writeUnshared()
and readUnshared()
methods may produce unexpected results.
Automated detection is straightforward.
Bibliography
[API 2011] | Classes ObjectOutputStream and ObjectInputStream |
...