Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

When objects are being serialized using the writeObject() method, each individual object is written to the output stream only once. Calls to Invoking the writeObject() method for on the second and subsequent occurrences, place references to the first occurrence 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 actual object instance for each object found present in the input stream ; references that was previously written by writeObject() become references to the first object found in the stream.

According to the Java API [API 20112013], 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 Consequently, the writeUnshared()/ and readUnshared() methods cannot be used are unsuitable 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.:

Code Block
public class Person {

    private String name;
    
    Person() {
        // doDo nothing - needed for serialization
    }
    
    Person(String theName) {
        name = theName;
    }
    
    public String getName () {
        return name;
    }

// 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 ArrayList<Student>List<Student> tutees = new ArrayList<Student>();
     
    Professor() {
        // doDo nothing - needed for serialization
    }
     
    Professor(String theName) {
        super(theName);
    }
     
    public ArrayList<Student>List<Student> getTutees () {
        return tutees;
    }
     
    /**
     * checkTutees checks that all the tutees
     * have this Professor as their tutor
     */
    public boolean checkTutees () {
        boolean result = true;
       for 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.  We then create

Suppose that Professor Jane who has three tuteesstudents, Able, Baker, and Charlie, all of whom have Professor Jane as their tutor.   The println() statement prints trueIssues can arise if the writeUnshared() and readUnshared() methods are used with these classes, as demonstrated in the following noncompliant code example.

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 objects from the original Professor object.

Code Block
bgColor#FFcccc
String filename = "serial";

 try {
    System.out.println("Serializing using writeUnshared");
    try(ObjectOutputStream oos = new ObjectOutputStream(new 
        (new FileOutputStream(filename))); {
  // Serializing         
  using writeUnshared
  oos.writeUnshared(jane);
} catch (Throwable  oos.close();e) {
  //          
    System.out.println("Handle error
}

// Deserializing using readUnshared");
    
try(ObjectInputStream ois = new ObjectInputStream(new 
        (new FileInputStream(filename)));
            
  {
  Professor jane2 = (Professor)ois.readUnshared();
    ois.close();
            
    System.out.println("checkTutees returns: " +
        jane3.checkTutees());
    // prints "checkTutees returns: false"
            jane2.checkTutees());
} catch (ExceptionThrowable e) {
  //  System.out.println("Exception during deserialization" + e);
}Handle error
}

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.

Compliant Solution

This compliant solution overcomes the problem of the noncompliant code example by using uses the writeObject() and readObject(), ensuring methods to ensure that the tutor object referred to by the three students has a one-to-one mapping with the original Professor object. The checkTutees() method correctly returns true.

Code Block
bgColor#ccccff
String filename = "serial";

try {
    System.out.println("Serializing using writeObject");
    ObjectOutputStream oos = new ObjectOutputStream(new 
        (new FileOutputStream(filename));
            
    ) {
	// Serializing using writeUnshared
	oos.writeObject(jane);
} catch (Throwable   oos.close();e) {
    //        
    System.out.println("Handle error
} 

// Deserializing using readObject");
    readUnshared
try(ObjectInputStream ois = new ObjectInputStream(new 
        (new FileInputStream(filename)));
            
     {
Professor jane2 = (Professor)ois.readObject();
    ois.close();
            
    System.out.println("checkTutees returns: " +
        jane2.checkTutees());
    // prints "checkTutees returns: true"
            jane2.checkTutees());
} catch catch(ExceptionThrowable e) {
    System.out.println("Exception during deserialization" + e);
}
// Handle error
} 

Applicability

Using the writeUnshared() and readUnshared() methods may produce unexpected results when used for the round-trip serialization of the data structures containing reference cycles.

Automated detection is straightforward.

Bibliography

[API 20112013]

Classes Class ObjectOutputStream and
Class ObjectInputStream

 

...

Image Modified Image Modified Image Modified