When objects are serialized using the writeObject()
method, each object is written to the output stream only once. Invoking the writeObject()
method on the same object a second time places a back-reference to the previously serialized instance in the stream. Correspondingly, the readObject()
method produces at most one instance for each object present in the input stream that was previously written by writeObject().
According to the Java API [API 2011], the writeUnshared()
method:
Writes writes an "unshared" object to the ObjectOutputStream. This method is identical to writeObject, except that it always writes the given object as a new, unique object in the stream (as opposed to a back-reference pointing to a previously serialized instance).
Correspondingly, the readUnshared()
method:
Reads 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()
/ and readUnshared()
methods cannot be used for round-trip serialization of data structures that require a one-to-one mapping between objects pre-serialization preserialization and objects post-deserializationpostdeserialization. One common example of such a data structure is a graph or network of objects that may contain reference cycles.
Consider the following code example.:
Code Block |
---|
public class Person { private String name; Person() { // doDo nothing - needed for serialization } Person(String theName) { name = theName; } // otherOther details not relevant to this example } public class Student extends Person implements Serializable { private Professor tutor; Student() { // doDo nothing - needed for serialization } Student(String theName, Professor theTutor) { super(theName); tutor = theTutor; } public Professor getTutor() { return tutor; } } public class Professor extends Person implements Serializable { private List<Student> tutees = new ArrayList<Student>(); Professor() { // doDo nothing - needed for serialization } Professor(String theName) { super(theName); } public List<Student> getTutees () { return tutees; } /** * checkTutees checks that all the tutees * have this Professor as their tutor */ public boolean checkTutees () { boolean result = true; for (Student stu: tutees) { if (stu.getTutor() != this) { result = false; break; } } return result; } } // ... Professor jane = new Professor("Jane"); Student able = new Student("Able", jane); Student baker = new Student("Baker", jane); Student charlie = new Student("Charlie", jane); jane.getTutees().add(able); jane.getTutees().add(baker); jane.getTutees().add(charlie); System.out.println("checkTutees returns: " + jane.checkTutees()); // printsPrints "checkTutees returns: true" |
Professor
and Students
are types that extend the basic type Person
. A student (i.e.that is, an object of type Student
) has a tutor of type Professor
. A professor (i.e.that is, 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.
Professor Jane who has three tutees, Able, Baker, and Charlie, all of whom have Professor Jane as their tutor. Issues can arise if if the writeUnshared()
and readUnshared()
methods are used with these classes, as demonstrated in the following noncompliant code example.
...
This noncompliant code example attempts to serialize the data from the previous 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 from the original Professor
object.
Code Block | ||
---|---|---|
| ||
String filename = "serial"; ObjectOutputStream oos = null; try { // Serializing using writeUnshared oos = new ObjectOutputStream(new FileOutputStream(filename)); oos.writeUnshared(jane); } catch (Exception e) { System.out.println("Exception during serialization" + e); } finally { try { oos.close(); } catch (IOException e) { System.out.println("Exception during serialization" + e); } } // Deserializing using readUnshared ObjectInputStream ois= null; try { ois = new ObjectInputStream(new FileInputStream(filename)); Professor jane2 = (Professor)ois.readUnshared(); System.out.println("checkTutees returns: " + jane2.checkTutees()); // printsPrints "checkTutees returns: false" } catch (Exception e) { System.out.println("Exception during deserialization" + e); } finally { try { ois.close(); } catch (IOException e) { System.out.println("Exception during deserialization" + e); } } |
...
Code Block | ||
---|---|---|
| ||
String filename = "serial"; ObjectOutputStream oos = null; try { // Serializing using writeUnshared oos = new ObjectOutputStream(new FileOutputStream(filename)); oos.writeObject(jane); } catch (Exception e) { System.out.println("Exception during serialization" + e); } finally { try { oos.close(); } catch (IOException e) { System.out.println("Exception during serialization" + e); } } // Deserializing using readUnshared ObjectInputStream ois= null; try { ois = new ObjectInputStream(new FileInputStream(filename)); Professor jane2 = (Professor)ois.readObject(); System.out.println("checkTutees returns: " + jane2.checkTutees()); // printsPrints "checkTutees returns: false" } catch (Exception e) { System.out.println("Exception during deserialization" + e); } finally { try { ois.close(); } catch (IOException e) { System.out.println("Exception during deserialization" + e); } } |
...
Using the writeUnshared()
and readUnshared()
methods may produce unexpected results.
Bibliography
[API 2011] | Classes Class ObjectOutputStream and |
...