| ★ wanayoo — archive 1999 http://developer.java.sun.com/developer/technicalArticles/Programming/serialization/ | Nouvelle recherche | Portail wanayoo |
|
|
|
Articles
Index
We all know the JavaTM platform allows us to create reusable objects in memory. However, all of those objects exist only as long as the JavaTM virtual machine1 remains running. It would be nice if the objects we create could exist beyond the lifetime of the virtual machine, wouldn't it? Well, with object serialization, you can flatten your objects and reuse them in powerful ways.
Discover the Secrets of the JavaTM Serialization APIby Todd Greanier; Reprinted from JavaWorldObject serialization is the process of saving an object's state to a sequence of bytes, as well as the process of rebuilding those bytes into a live object at some future time. The Java Serialization API provides a standard mechanism for developers to handle object serialization. The API is small and easy to use, provided the classes and methods are understood. Throughout this article, we'll examine how to persist your Java objects, starting with the basics and proceeding to the more advanced concepts. We'll learn three different ways to perform serialization -- using the default protocol, customizing the default protocol, and creating our own protocol -- and we'll investigate concerns that arise with any persistence scheme such as object caching, version control, and performance issues. By the conclusion of this article, you should have a solid comprehension of that powerful yet sometimes poorly understood Java API.
First Things First: The Default MechanismLet's start with the basics. To persist an object in Java, we must have a persistent object. An object is marked serializable by implementing thejava.io.Serializable interface, which signifies to the underlying API that the object can be flattened into bytes and subsequently inflated in the future.
Let's look at a persistent class we'll use to demonstrate the serialization mechanism:
As you can see, the only thing we had to do differently from creating a normal class is implement the
Rule #1: The object to be persisted must implement the
The next step is to actually persist the object. That is done with the
Take a look at the code used to save the
The real work happens on line 200 when we call the To restore the file, we can employ the following code:
In the code above, the object's restoration occurs on line 210 with the
Later, on line 360, we simply call the
Nonserializable ObjectsThe basic mechanism of Java serialization is simple to use, but there are some more things to know. As mentioned before, only objects markedSerializable can be persisted. The java.lang.Object class does not implement that interface. Therefore, not all the objects in Java can be persisted automatically. The good news is that most of them -- like AWT and Swing GUI components, strings, and arrays -- are serializable.
On the other hand, certain system-level classes such as
That situation presents a problem: what if we have a class that contains an instance of Let's assume we want to create a class that performs an animation. I will not actually provide the animation code here, but here is the class we'll use:
When we create an instance of the Therefore, we have another rule to add. Here are both rules concerning persistent objects:
Customize the Default ProtocolLet's move on to the second way to perform serialization: customize the default protocol. Though the animation code above demonstrates how a thread could be included as part of an object while still making that object be serializable, there is a major problem with it if we recall how Java creates objects. To wit, when we create an object with thenew keyword, the object's constructor is called only when a new instance of a class is created. Keeping that basic fact in mind, let's revisit our animation code. First, we instantiate an object of type PersistentAnimation, which begins the animation thread sequence. Next, we serialize the object with that code:
All seems fine until we read the object back in with a call to the
Well, there is good news. We can make our object work the way we want it to; we can make the animation restart upon restoration of the object. To accomplish that, we could, for example, create a There is, however, a strange yet crafty solution. By using a built-in feature of the serialization mechanism, developers can enhance the normal process by providing two methods inside their class files. Those methods are:
Notice that both methods are (and must be) declared
Considering all that, let's look at a revised version of
Notice the first line of each of the new private methods. Those calls do what they sound like -- they perform the default writing and reading of the flattened object, which is important because we are not replacing the normal process, we are only adding to it. Those methods work because the call to Those private methods can be used for any customization you need to make to the serialization process. Encryption could be added to the output and decryption to the input (note that the bytes are written and read in cleartext with no obfuscation at all). They could be used to add extra data to the stream, perhaps a company versioning code. The possibilities are truly limitless.
Stop That Serialization!OK, we have seen quite a bit about the serialization process, now let's see some more. What if you create a class whose superclass is serializable but you do not want that new class to be serializable? You cannot unimplement an interface, so if your superclass does implementSerializable, your new class implements it, too (assuming both rules listed above are met). To stop the automatic serialization, you can once again use the private methods to just throw the NotSerializableException. Here is how that would be done:
Any attempt to write or read that object will now always result in the exception being thrown. Remember, since those methods are declared
Create Your Own Protocol: the Externalizable InterfaceOur discussion would be incomplete not to mention the third option for serialization: create your own protocol with theExternalizable interface. Instead of implementing the Serializable interface, you can implement Externalizable, which contains two methods:
Just override those methods to provide your own protocol. Unlike the previous two serialization variations, nothing is provided for free here, though. That is, the protocol is entirely in your hands. Although it's the more difficult scenario, it's also the most controllable. An example situation for that alternate type of serialization: read and write PDF files with a Java application. If you know how to write and read PDF (the sequence of bytes required), you could provide the PDF-specific protocol in the
Just as before, though, there is no difference in how a class that implements
GotchasThere are a few things about the serialization protocol that can seem very strange to developers who are not aware. Of course, that is the purpose of the article -- to get you aware! So let's discuss a few of those gotchas and see if we can understand why they exist and how to handle them.
Caching Objects in the StreamFirst, consider the situation in which an object is written to a stream and then written again later. By default, anObjectOutputStream will maintain a reference to an object written to it. That means that if the state of the written object is written and then written again, the new state will not be saved! Here is a code snippet that shows that problem in action:
There are two ways to control that situation. First, you could make sure to always close the stream after a write call, ensuring the new object is written out each time. Second, you could call the
Version ControlWith our second gotcha, imagine you create a class, instantiate it, and write it out to an object stream. That flattened object sits in the file system for some time. Meanwhile, you update the class file, perhaps adding a new field. What happens when you try to read in the flattened object?
Well, the bad news is that an exception will be thrown -- specifically, the
Yes, but it takes a little code manipulation. The identifier that is part of all classes is maintained in a field called
Here is an example of using
Simply copy the returned line with the version ID and paste it into your code. (On a Windows box, you can run that utility with the
The version control works great as long as the changes are compatible. Compatible changes include adding or removing a method or a field. Incompatible changes include changing an object's hierarchy or removing the implementation of the
Performance ConsiderationsOur third gotcha: the default mechanism, although simple to use, is not the best performer. I wrote out aDate object to a file 1,000 times, repeating that procedure 100 times. The average time to write out the Date object was 115 milliseconds. I then manually wrote out the Date object, using standard I/O the same number of iterations; the average time was 52 milliseconds. Almost half the time! There is often a trade-off between convenience and performance, and serialization proves no different. If speed is the primary consideration for your application, you may want to consider building a custom protocol.
Another consideration concerns the aforementioned fact that object references are cached in the output stream. Due to that, the system may not garbage collect the objects written to a stream if the stream is not closed. The best move, as always with I/O, is to close the streams as soon as possible, following the write operations.
ConclusionSerialization in Java is simple to instigate and almost as simple to implement. Understanding the three different ways of implementing serialization should aid in bending the API to your will. We have seen a lot of the serialization mechanism in that article, and I hope it made things clearer and not worse. The bottom line, as with all coding, is to maintain common sense within the bounds of API familiarity. That article has laid out a strong basis of understanding the Java Serialization API, but I recommend perusing the specification to discover more fine-grained details.
Reprinted with permission from the June 2000 edition of JavaWorld magazine. Copyright ITworld.com, Inc., an IDG Communications company. Register for editorial e-mailalerts
About the AuthorTodd Greanier,director of technology for ComTech Training, has been teaching and developing Java since it was introduced publicly. An expert in distributed Java technologies, he teaches classes in a wide range of topics, including JDBCTM, RMI, CORBA, UML, Swing, servlets/JSPTM, security, JavaBeansTM, Enterprise Java BeansTM, and multithreading. He also creates custom seminars for corporations, slanted to their specific needs. Todd lives in upstate New York with his wife, Stacey, and his cat, Bean. Reader FeedbackTell us what you think of this article.
_______ |