19

It's said that Java's default serialization mechanism is not very efficient because a)it discovers which fields to write/read through reflection which is usually slow b) it writes extra data to stream.

One way to make it more efficient is to implement Externalizable and its writeExternal/readExternal methods.

Here's the question: if I instead provide 'writeObject/readObject' methods and don't call deafiltWriteObject/defaultReadObject in them, then this mechanism won't use reflection to figure out which fields to write/read, plus it won't write extra data to stream (or will it? not sure). So from an efficiency perspective, is implementing writeObject/readObject mentioned above same as implementing Externalizable? Or does the latter option give some more practical benefits which the former doesn't?

EDIT: a difference, ofcourse, is when a Serializable class implementing readObject/writeObject is subclassed, and if the subclass has its own readObject/writeObject, they don't need to call super's readObject/writeObject. Not so if the super/subclass instead implement Externalizable. In this case, super's writeExternal/readExternal need to be explicitly called. However, this difference is irrelevant from an efficiency point of view.

shrini1000
  • 7,038
  • 12
  • 59
  • 99
  • may i know how you came to the conclusion that super.readObject() is not required in serializable class? does this mean for every read/write Object method the compiler adds a call to defaultread/writeObject() method in first line? – amarnath harish Jul 24 '18 at 10:05
  • @amarnath harish, I believe the compiler adds a call to super's read/write object. You can write a small code snippet to verify this. Pl. let me know if you find otherwise. – shrini1000 Sep 01 '18 at 16:14

3 Answers3

9

There is still some over head in choosing which class/writeObject/readObject to call next. but it is significantly reduced.

This can perform the same as Externalizable depending on what you are doing and whether you use the extra options it gives you. e.g. readObject assumes you to create a new object each time, Externalizable has readResolve which means you can reuse objects.

http://docs.oracle.com/javase/1.5.0/docs/guide/serialization/spec/input.html

In many cases, recycling objects is the "next" step in speeding up deserialization. (Assuming that's an option for you)

http://vanillajava.blogspot.co.uk/2011/10/recycling-objects-to-improve.html

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • Could you pl. explain this a bit more: "There is still some over head in choosing which class/writeObject/readObject to call next." Also, 'readResolve' isn't a part of 'Externalizable' contract, right? So I'm a bit confused. – shrini1000 May 04 '12 at 07:01
  • It has to determine which class to call readObject or readExternal unless you also do this yourself. readResolve is an optional method which you can use with Serailizable as well. See the first link for more details. – Peter Lawrey May 04 '12 at 08:02
  • 1
    Thx. I've used readResolve, but got confused because you mentioned it in connection with Externalizable. Btw, I read your article. Good one. I've asked a question there. It'd be great if you could pl. reply. – shrini1000 May 04 '12 at 08:06
2

Found a couple of things while experimenting and going through serialization mechanism's code:

1) if the object is found to be Externalizable, it's cast to Externalizable, and corresponding method is called on it; whereas for Serializable object, it's reflectively checked if it has readObject/writeObject. So maybe this makes it slightly slower,

2) the amount of data written for Externalizable is a little less than Serializable with readObject/writeObject (I found a difference of 1 byte for the following code when I wrote object of B).

For Externalizable:

static class A implements Externalizable
{
    @Override
    public void writeExternal(ObjectOutput out) throws IOException 
    {
        System.out.println("A write called");
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException 
    {
        System.out.println("A read called");
    }       
}

static class B extends A implements Externalizable
{       
    @Override
    public void writeExternal(ObjectOutput out) throws IOException 
    {
        super.writeExternal(out);
        System.out.println("B write called");
    }

    @Override
    public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException 
    {
        super.readExternal(in);
        System.out.println("B read called");
    }       
}

For Serializable:

static class A implements Serializable
{
    private void writeObject(ObjectOutputStream out) throws IOException 
    {
        System.out.println("A write called");
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException 
    {
        System.out.println("A read called");
    }       
}

static class B extends A implements Serializable
{       
    private void writeObject(ObjectOutputStream out) throws IOException 
    {           
        System.out.println("B write called");
    }

    private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException 
    {           
        System.out.println("B read called");
    }       
}
shrini1000
  • 7,038
  • 12
  • 59
  • 99
1

The major difference, in terms of class design, is that Serializable will work on any class, whereas Externalizable only works on mutable classes with public default (no-arg) constructors.

nilskp
  • 3,097
  • 1
  • 30
  • 34
  • Do they really need to be mutable? I mean, can't I do something like this? http://www.byteslounge.com/tutorials/java-externalizable-example – shrini1000 Apr 14 '15 at 12:53
  • @shrini1000, you could do that, but the `User` class is not thread-safe. However making all the fields `volatile` should fix that, so in the end, you're right, the class does not, strictly speaking, have to be mutable. – nilskp Apr 14 '15 at 17:01