shaikezam.com

Serialized And Deserialized Objects

26-12-2019 - Shai Zambrovski


Java have a build-in mechanism that can migrate objects to stream of bytes and store them in a file and vice versa.

Those abilities called Serialization and Derialization.

Serialization and Derialization are platfrom independent, that means that we can serialize in one platfrom and deserialize in a different platform.

In order to use this mechanism:

Please notice, Serializable interface is a marker interface:

An empty interface (no field or methods) that provides run-time type information about objects. We will demostrate this mechanism using simple Course class: We will demostrate this mechanism using simple Course class:

public class Course implements Serializable {
    private String name;
    private String location;
    private transient int maxStudentsCapacity;

    public Course(String name, String location, int studentsCapacity) {
        this.name = name;
        this.location = location;
        this.maxStudentsCapacity  = studentsCapacity;
    }
}

Serializing

For serializing an object we need to use the java.io.ObjectOutputStream class :

Course javaCourse = new Course("Java Fundamentals", "Auditorium", 50);
try(ObjectOutputStream os = new ObjectOutputStream(Files.newOutputStream(Paths.get("course.dat")))) {
    os.writeObject(javaCourse);
} catch (IOException e) {...}

Deserializing

For deserializing an object we need to use the java.io.ObjectInputStream class:

try(ObjectInputStream is = new ObjectInputStream(Files.newInputStream(Paths.get("course.dat")))) {
    Course javaCourse = (Course) is.readObject();
} catch (IOException e) {...}
  catch (ClassNotFoundException e) {...}

If we print the javaCourse we see that the maxStudentsCapacity property is 0 altough we set it to 50.

That because this field marked as transient and it didn’t write to the course.dat file, and 0 is the default int value.

Serializing Version Compatibility

If we take a look on a very reasonable scenario in-which we want to add to our Course class a courseYear property that will holds the year of the course:

public class Course implements Serializable {
    private String name;
    private String location;
    private transient int maxStudentsCapacity;
    private String courseYear;

    public Course(String name, String location, int maxStudentsCapacity, String courseYear) {
        this.name = name;
        this.location = location;
        this.maxStudentsCapacity = maxStudentsCapacity;
        this.courseYear = courseYear;
    }
}

And now, lets try to Deserializing again the course.dat file again, we will get:

java.io.InvalidClassException: com.company.Course; local class incompatible: stream classdesc serialVersionUID = -3353359511573316605, local class serialVersionUID = 498427908211648568

We get an execption because the Course class definition we trying to Deserializing is very different from the newley class definition.

One class has the serial version UID -3353359511573316605, while the other class has the serial version UID 498427908211648568, and that why we get this execption.

The serial version UID it’s a private, static final, long class property that indicates version compatibility (all version must have the same value), and it can be calculated through two ways:

To overcome the InvalidClassException issue there are two ways:

we need to pass the full class name: com.company.Course and click show:

And now, if we Deserializing again the course.dat file we will get:

Course{name='Java Fundamentals', location='Auditorium', maxStudentsCapacity=0, courseYear='null'} Please notice that courseYear value is 'null', because this is the default value of String, let’s understand how we can override it.

Customizing Serializing

We can Customizing Serializing and Deserializing using writeObject and readObject methods in which called through reflection.

Can be very useful we we want to change the format of the Serializing or to support for older versions of serialized classes.

public class Course implements Serializable {
    private String name;
    private String location;
    private transient int maxStudentsCapacity;
    private String courseYear;

    public Course(String name, String location, int maxStudentsCapacity, String courseYear) {
        this.name = name;
        this.location = location;
        this.maxStudentsCapacity = maxStudentsCapacity;
        this.courseYear = courseYear;
    }


    private void writeObject(ObjectOutputStream os) throws IOException, ClassNotFoundException {
        os.defaultWriteObject();
    }

    private void readObject(ObjectInputStream is) throws IOException, ClassNotFoundException {
        ObjectInputStream.GetField fields = is.readFields();
        this.name = (String) fields.get("name", "defaultName");
        this.location = (String) fields.get("location", "defaultlocation");
        this.courseYear = (String) fields.get("courseYear", "defaultCourseYear");
    }
}

readObject

To override the values in which returns from the Deserializing we need to get the fields and pass as the 2nd argument the default values.

writeObject

Very useful for custom Serializing if we want to override some property before we Serializing.