Into Java - Part XVI

By Simon Grönlund

Into Java, Part 16
Today we will explore a convenient way to store and retrieve your application data, something that can be really tiresome in other programming languages, but is a breeze with Java. In fact any data may be stored this way, basic data types or classes built by you or Java default classes. More than that, this is a rather fast way to store and read data from disk as well as from other streams. And it is by far the easiest technique to implement.

As we have said, streams may take several forms and today we will look into object streams, both input and output. To handle them we must learn about Serializable, but don't worry, that is only a declarative interface with no methods to take care of.

As our application example we will use our old PaintBox. Until today we have not been able to save our paintings, but now we will enable that using a menu bar item. Yes, we will add menu bars to our repertoire. Using the Swing JMenuBar that is also a piece of cake, as are most of the graphic tools in Swing.

Serialization interface
The object stream saves the data to a particular file format not easily read by human eyes. The file format is not important, we do not need to know about its inner anatomy. In fact, we do not need to know that object streams use a file format of their own. But we do have to know that we must use the Serializable interface that resides in the java.io package. It is an empty interface that only tells the Java compiler that we know that this object may get serialized. We need to add implement Serializable to our class header. That is it, you are done!

Briefly though, when an object stream saves your object(s) it needs to save a description of the class, further it saves a seal and some more details, I will not go into the details of that. Important to know though, is that it is the object's state that is saved, that is, any instance variable is saved. This is a good reason to use instance variables sparsely, many times only a few are necessary. Think "do I need to save this variable to get an object back on its feet again?" If the answer is "yes" it needs to be an instance variable, but if the value may be computed from other instance variables it is not a good candidate.

Sometimes we do not want to save an instance variable in any case, perhaps for security reasons or if it holds vital transient information. Perhaps we can ask for it or recompute it, for example machine related information as window sizes, or a value of limited life time. In these cases we use the keyword transient, as /* a value that is useless on another machine with other screen resolutions */ private transient int windowWidth; Since it is possible, though tiresome, to exploit the information of the stream, it is good to use transient for any variable you do not want to reveal, or possibly that needs to be encrypted.

The observant reader will notice that the same reasoning must hold for variables declared static that is a class variable common to every instance of the class, while an instance variable holds the state of a distinct object. If we need to recall the value of a static variable we need to take care of that on our own. Having covered this use of static and transient, I consider this topic closed for now.

Finally, reading an object back from an object stream never calls any method nor the constructor, it simply reinstates the proper values in the reincarnated object. Hence, nothing is computed at the rebirth. If this is needed, find some deeper readings on this topic to find out how you can do that.

Reading and writing objects
The name "object stream" implies that only objects may be written and/or read to such streams. That is not true, the methods xxxInt, xxxDouble</tt>, etc., (where xxx is replaced by read</tt> or write</tt>) take care of the basic data types.

While basic data types are read and written with specific methods, reading and writing objects is done with the xxxObject</tt> methods. As with getting objects back from data structures, we must apply a cast to the returned value, which is of the basic Object</tt> type, to the type we know it should be.

If using this technique for reading and writing data one by one, it is important that the read-back is done in exactly the same order the writing was done, using the matching methods. When you have a lot of data, this is really tiresome. In a moment I will show you a more convenient way.

There is much more to tell on this topic, but I think that will be beyond the scope of this column. One good resource is "Core Java 2, Volume 1", by Horstmann & Cornell, starting at page 664. I would like to mention that there are several ways to twist both how data and what data should be written to the stream. Naturally in such cases we must use refined techniques to get the data back again. The door is open, you decide your direction.

ObjectOutputStream and ObjectInputStream
In our example application, the PaintBox</tt>, add two lines in the Paintshape</tt> class. First we must import java.io</tt> and then tell Java that we want to implement the behaviour of Serializable</tt> objects. In this class there are no transient variables or anything else to consider.



Now we are ready with the class PaintShape</tt>. Let us continue with PaintPanel</tt> and see how we use object streams.

As expected, both ObjectInputStream</tt> and ObjectOutputStream</tt> are located in java.io</tt> which needs to be imported. We will first look into how to save our PaintShape</tt> objects.

We will simply make a stream, and then write the objects to that stream. Add a small method saveFile</tt>, or any name you like, somewhere in the <tt>PaintPanel</tt> class, as the figure beneath shows. We do not have to implement <tt>Serializable</tt> in this class since this class is not going to be saved, we are only interested in saving the painted shapes, aren't we?



If you would like to test it so far, add a single line to the <tt>PaintBox</tt> class:
 * 1) find the constructor we named <tt>firstInit</tt>.
 * 2)  as the very first line of the <tt>windowClosing</tt> method block, add <tt>paintArea.saveFile;</tt> immediately before the <tt>System.exit(0)</tt> line.

This new line will save the painting you sketched when you are exiting the program. Of course you are still not happy with that, there is no way to get the painting back. But in any case, you may find the <tt>paint.dat</tt> file saved and you can inspect it a little. Some parts of it are readable, but most of it is not.

Please look back into the code. First we create the object output stream, this time wrapped around a <tt>FileOutputStream</tt>. Recall that an object stream shall be wrapped around anything extending a stream, <tt>InputStream</tt> or <tt>OutputStream</tt>.

Kindly note that we are writing the entire <tt>Vector storedShapes</tt> to the stream. We need not save one shape after another or use another similar scheme, it is possible to save a complete data structure. And if it is, for example, sorted in a certain way, upon reading it back later on, it will still be sorted in the very same way. If there are dependencies between the stored objects, they will be valid after they are read back. Convenient, huh?

Would you like to get the painting back? It is as simple as saving it. Still in the <tt>PaintPanel</tt> we add a little method like this one.



As with the former method, we have to catch <tt>IOException</tt> if anything goes wrong with the file. But another exception is introduced, <tt>ClassNotFoundException</tt>, that is thrown when a class referred to in the object file is not present as a class file. Hence we find it necessary to always bring the class files along with the object file, they need each other. Later on, erase the <tt>PaintShape.class </tt> file and try to read the data file.

After creating the object input stream around a <tt>FileInputStream</tt> we read from it. Again please note how conveniently it is done, we simply read an object from the stream and assign the object to an instance variable <tt>storedShapes</tt>, that is the <tt>Vector</tt> we used when saving the painting. But since <tt>readObject</tt> returns an <tt>Object</tt> we need to apply a cast to a <tt>Vector</tt>, as we know that <tt>storedShapes</tt> is declared to be of type <tt>Vector</tt>.

Assigning a new vector to the old variable is simply a replacement, we replace the old data structure (a <tt>Vector</tt>) with a new one.

Would you like to go for a test ride? Then simply add the line paintArea.readFile; as the very last line of the method <tt>init</tt> in the <tt>PaintBox</tt> class, after that the <tt>paintArea</tt> object is instantiated. That is, as soon as the canvas is ready we call the <tt>readFile</tt> method which replaces the still empty <tt>storedShapes Vector</tt> with a new stuffed one at start-up time. Try that, and say... Wow!

If you followed the instructions exactly, you now can save your paintings. Unfortunately only one, and you can not erase the data file since that will crash the program at start, due to an <tt>IOException</tt>. We need a better way to call the save and read methods, don't we?

Menu bar
Adding a menu bar is not hard, but adding a lot of menus and a lot of items to each menu can be tiresome (as the example shows). Today we will only add one menu with four menu items added to it. Here comes one example of what that might look like:



Setting up a menu bar is only done once, as there is only one menu bar in an application. The first two lines create one and set it to the frame, that is <tt>setJMenuBar(bar)</tt> is called from within the class extending <tt>JFrame</tt>. In our case that will be <tt>PaintBox</tt>. I have chosen to put the entire code within a method of its own, <tt>secondInit</tt>, which we can call from the <tt>PaintBox</tt> constructor as the second line, between <tt>firstInit</tt> and <tt>init</tt>.

The second part of the code is to create a menu, the "File" menu, which we simply add to the menu bar. Having more menus is done the same way, adding each to the same menu bar. Of course, menus may have fancy stuff like mnemonics and so forth, but I will omit that for now.

Then there is the first real menu item created, the "Open 'paint.dat'" item. So far there is nothing to worry about, but of course menus may have icons, mnemonics, key accelerators and so forth, too. But for now we must recall how to add a listener to that item. That is done by adding an <tt>ActionListener</tt> instantiated on the fly, supplying an <tt>actionPerformed</tt> method. Finally we add the menu item to the menu.

Each menu item contains an <tt>ActionListener</tt> object, but it would also be fine to make the whole class <tt>implement ActionListener</tt> and add the method <tt>actionPerformed</tt>, containing a few <tt>if - if else</tt> tests with the very same method calls. The choice is yours, and we have tested both ways in previous IntoJava instalments.

As seen in the code above, we do call the two methods we created in the <tt>PaintPanel</tt> class. We call <tt>System.exit(0)</tt> once. We added two separators, for sharpness, and in order to learn how. But we have not seen the method <tt>clearShapes</tt> that seems to be resident in <tt>PaintPanel</tt>. Here it comes, a convenient way to start over with a new painting:



It certainly is easy with Java, isn't it? Only a single call to the prefabricated <tt>Vector</tt> class removes every shape we have made, and we are ready for another sketch. Unfortunately I still do not know how to overcome that the last item is not always cleared, but vanishes as soon as you start to paint a new object. Anyone have an idea? [mailto:d97-sgr@d.kth.se Call me]!

Do not forget to remove the lines you added before when testing the first two methods. Compile and go!

Summary
Here you have a little application capable of saving your small, cute paintings. And restoring them. Unfortunately you have to use your favourite file manager to copy them into other file names if you (or your kids) like to do a lot of them. So next time we will look into how to use the <tt>JFileChooser</tt>. At the same time we will briefly look into the handy dialogs that come with Swing, the <tt>JOptionPane</tt>.

As a convenience I have included all the necessary Java files as they looked before we started with this column: We have learned how to make easy use of object streams, and that they can handle complete data structures in a pinch. Naturally we can use these streams a lot more, and we will. We will also, in a future column, see how convenient they are when sending data over a network, LAN or WAN.
 * PaintBox.java
 * PaintPanel.java
 * PaintShape.java
 * SlidePanel.java
 * TopButtonPanel.java

We have also learned how to add menus to our applications, it looked tiresome, and it sure is most of the time. But not that hard, don't you think?

CU next month.