Comparing Externalizable to Serializable

This is true even if the superclass implements Serializable . The metadata about the class structure will be written to the stream, but the serialization mechanism will not be invoked. This can be useful if, for some reason, you want to avoid using serialization with the superclass. For example, some of the Swing classes, [ 8] while they claim to implement Serializable , do so incorrectly and will throw exceptions during the serialization process. If you really need to use these classes, and you think serialization would be useful, you may want to think about creating a subclass and declaring it to be Externalizable . Instances of your class will be written out and read in using externalization. Because the superclass is never serialized or deserialized, the incorrect code is never invoked, and the exceptions are never thrown. [ 8] JTextArea is one of the most egregious offenders.

10.7.1 Comparing Externalizable to Serializable

Of course, this efficiency comes at a price. Serializable can be frequently implemented by doing two things: declaring that a class implements the Serializable interface and adding a zero-argument constructor to the class. Furthermore, as an application evolves, the serialization mechanism automatically adapts. Because the metadata is automatically extracted from the class definitions, application programmers often dont have to do anything except recompile the program. On the other hand, Externalizable isnt particularly easy to do, isnt very flexible, and requires you to rewrite your marshalling and demarshalling code whenever you change your class definitions. However, because it eliminates almost all the reflective calls used by the serialization mechanism and gives you complete control over the marshalling and demarshalling algorithms, it can result in dramatic performance improvements. To demonstrate this, I have defined the EfficientMoney class. It has the same fields and functionality as Money but implements Externalizable instead of Serializable : public class EfficientMoney extends ValueObject implements Externalizable { public static final long serialVersionUID = 1; protected int _cents; public EfficientMoneyInteger cents { thiscents.intValue ; } public EfficientMoneyint cents { supercents + cents.; _cents = cents; } public void readExternalObjectInput in throws IOException, ClassNotFoundException { _cents = in.readInt ; _stringifiedRepresentation = _cents + cents.; } public void writeExternalObjectOutput out throws IOException { out.writeInt_cents; } } We now want to compare Money with EfficientMoney . Well do so using the following application: public class MoneyWriter { public static void mainString[] args { writeOne ; writeMany ; } private static void writeOne { try { System.out.printlnWriting one instance; Money money = new Money1000; writeObjectC:\\temp\\foo, money; } catchException e{} } private static void writeMany { try { System.out.printlnWriting many instances; ArrayList listOfMoney = new ArrayList ; for int i=0; i10000; i++ { Money money = new Moneyi100; listOfMoney.addmoney; } writeObjectC:\\temp\\foo2, listOfMoney; } catchException e{} } private static void writeObjectString filename, Object object throws Exception { FileOutputStream fileOutputStream = new FileOutputStreamfilename; ObjectOutputStream objectOutputStream = new ObjectOutputStreamfileOutputStream; long startTime = System.currentTimeMillis ; objectOutputStream.writeObjectobject; objectOutputStream.flush ; objectOutputStream.close ; System.out.printlnTime: + System.currentTimeMillis - startTime; } } On my home machine, averaging over 10 trial runs for both Money and EfficientMoney , I get the results shown in Table 10- 1 . [ 9] [ 9] We need to average because the elapsed time can vary it depends on what else the computer is doing. The size of the file is, of course, con stant. Table 10-1. Testing Money and EfficientMoney Class Number of instances File size Elapsed time Money 1 266 bytes 60 milliseconds Money 10,000 309 KB 995 milliseconds EfficientMoney 1 199 bytes 50 milliseconds EfficientMoney 10,000 130 KB 907 milliseconds These results are fairly impressive. By simply converting a leaf class in our hierarchy to use externalization, I save 67 bytes and 10 milliseconds when serializing a single instance. In addition, as I pass larger data sets over the wire, I save more and more bandwidth™on average, 18 bytes per instance. Which numbers should we pay attention to? The single - instance costs or the 10,000-instance costs? For most applications, the single-instance cost is the most important one. A typical remote method call involves sending three or four arguments usually of different types and getting back a single return value. Since RMI clears the serialization mechanism between calls, a typical remote method call looks a lot more like serializing 3 or 4 single instances than serializing 10,000 instances of the same class. If I need more efficiency, I can go further and remove ValueObject from the hierarchy entirely. The ReallyEfficientMoney class directly extends Object and implements Externalizable : public class ReallyEfficientMoney implements Externalizable { public static final long serialVersionUID = 1; protected int _cents; protected String _stringifiedRepresenta tion; public ReallyEfficientMoneyInteger cents { thiscents.intValue ; } public ReallyEfficientMoneyint cents { _cents = cents; _stringifiedRepresentation = _cents + cents.; } public void readExternalObjectInput in throws IOExcep tion, ClassNotFoundException { _cents = in.readInt ; _stringifiedRepresentation = _cents + cents.; } public void writeExternalObjectOutput out throws IOException { out.writeInt_cents; } } ReallyEfficientMoney has much better performance than either Money or EfficientMoney when a single instance is serialized but is almost identical to EfficientMoney for large data sets. Again, averaging over 10 iterations, I record the numbers in Table 10- 2 . Table 10-2. Testing ReallyEfficientMoney Class Number of instances File size Elapsed time ReallyEfficientMoney 1 74 bytes 20 milliseconds ReallyEfficientMoney 10,000 127 KB 927 milliseconds Compared to Money , this is quite impressive; Ive shaved almost 200 bytes of bandwidth and saved 40 milliseconds for the typical remote method call. The downside is that Ive had to abandon my object hierarchy completely to do so; a significant percentage of the savings resulted from not including ValueObject in the inheritance chain. Removing superclasses makes code harder to maintain and forces programmers to implement the same method many times ReallyEfficientMoney cant use ValueObject s implementation of equals and hashCode anymore. But it does lead to significant performance improvements.

10.7.2 One Final Point