Objectives In this chapter, we discuss the issues involved in designing application
frameworks and the use of design patterns. We study three important frameworks
in the Java Class Library - the Collections framework, the Input/Output
framework and the Graphical User Interface (GUI) framework.
Contents 6.1 Application Frameworks
6.2 The Collections Framework
6.2.1 Abstract Collections
6.2.2 Interfaces of Abstract Collections
6.2.3 Concrete Collections
6.2.4 Iteration
6.2.5 Ordering and Sorting
6.3 The Graphical User Interface Framework
6.3.1 The Widgets
6.3.2 Layout Managers
6.3.3 Handling Events
6.3.4 Frames and Dialogs
6.4 The Input/Output Framework
6.4.1 The Byte Streams
6.4.2 The Character Streams
6.4.3 Random Access Files
This is an extension of the program Words in Example 3.7. In Words,
we break the text input into words. In this example, we first add a simple
counter to count the total number of words in the text input. If a word
occurs multiple times, it will be counted multiple times. To count the
number of different words in the text input, we must remember the words
that have already occurred. A set serves this purpose well. We use a HashSet
to store the different words in the text input. Each word occurs only once
in the set even if it occurs multiple times in the input text.
This example demonstrates iterating through the entry set of a map. In
order to count the number of occurances of each word, we need a map that
maps each word to the number of its occurance. To print out the result,
we first obtain the entry set view of the map. We then iterate through
the entry set and print out each entry.
This example demonstrates the use of a sorted map ordered according to
the natural order of its keys. This is a variation of Example 6.2.
The words are the keys of the map, and the alphabetical order of words
is the natural order of String. So we can simply use a TreeMap, which by
default maintains its entries according to the natural order of its keys.
This example demonstrates the use of a sorted map ordered according to
a user-defined order on the keys. This is another variation of Example
6.2. To sort the entries according to an order other than the natural
order of the keys, we need to provide a user-defined comparator. An instance
of TreeMap can be instantiated by specifying the user-defined order in
the constructor. The following is the comparator for the reverse alphabetical
order of strings. To obtain the reverse alphabetical order, we simply negate
the sign of the result of the compareTo() method of the String class.
This is another variation of Example 6.2. In this case, the desired
order is not based on keys but based on the values, so sorted maps offer
no help. We will use a HashMap to store the word counts. After all the
words are counted, we obtain a collection view of the values contained
in the map, then sort the value collection according to a user-defined
comparator CountComparator.
This example demonstrates the flow layout, and the creation of buttons.
The Applet class is a subclass of Panel so the drawing area of an applet
is actually a container. We can add any component to the panel. Here, we
simply create and add six buttons to the panel. Note the correspondence
between the order of insertion and order of buttons in the layout.
This example demonstrates the grid layout. This program is rather similar
to the one in Example 6.6 The main difference is that a grid layout
manager is used here. The number of rows and columns of the grid are specified
as the parameters of the applet.
This example demonstrates the nesting of containers, and the creation of
choices and labels. The top level panel contains two nested panels. The
top level panel uses a border layout manager. Three buttons are inserted
into the north, east, and west region of the top level panel. The center
region of the top level panel contains another panel, which also uses a
border layout manager. A button is inserted to each of the five regions
of the panel at the center. The south region of the top level panel contains
yet another panel, which uses a flow layout manager. A button, a choice,
and a label are inserted into the panel in the south region.
This example demonstrates event handling. This program extends the NestedPanel
class in Example 6.9 by adding event handling. When one of the buttons
is clicked or one of the items in the choice is selected, a message is
displayed in the message bar, which is a label. The class NestedPanels2
is the listener of both types of events and listens to all the buttons
and the choice contained in the nested panel. The event class for button
click is ActionEvent and the associated listener interface is ActionListener.
The event class for choice item selection is ItemEvent and the associated
listener interface is ItemListener.
This example demonstrates the use of inner classes as event listeners.
This program behaves the same as the one in the previous example. It uses
two inner classes: one handles the button clicks and the other handles
the choice item selections.
This example demonstrates factorization by delegation, and the use of buttons
and choices to control animations. This is an enhancement of the bouncing
ball applets in Example 4.4. Three components are added to the original
applet to control the animation:
A start and a stop button to start and stop the animation.
The frame of the on-line ordering app contains a two components: an image
label, which consists of an image and a text, and the "order" button. When
the "order" button is pushed, a dialog box pops up. The dialog box has
three compartments:
The top compartment contains text labels and text fields. The layout
of the labels and the text fields are not handled by a layout manager.
Rather it is a customized layout that is specified using the absolute position
and dimension of each component.
The middle compartment has a titled border with the title "Credit Card."
It contains three check boxes that are exclusive.
The bottom compartment has an etched border. It contains the "Ok" and
"Cancel" button. An order is completed when the "Ok" button is pushed,
and the order information will be printed to the standard output.
Example 6.14: The "WriteMatrix1/ReadMatrix1" Application
This example demonstrates reading and writing data using the primitive
IO capabilities provided in the FileInputStream and FileOutputStream. WriteMatrix1
writes a 2 x 3 matrix to a file. It first writes the number of rows and
columns of the matrix followed by the numbers in the matrix. ReadMatrix1
reads the file written by WriteMatrix1 and restores the matrix.
Example 6.15: The "WriteMatrix2/ReadMatrix2" Application
This example demonstrates the use of IO decorators DataInputStream and
DataOutputStream. The WriteMatrix2 and ReadMatrix2 classes perform the
same tasks as the WriteMatrix1 and ReadMatrix1 classes in the previous
example. The main differences are the construction of the data input and
output streams, which are shown in bold face below. The writeInt(), writeDouble(),
readInt(), and readDouble() methods in the previous examples are no longer
needed.
Example 6.16: The "WriteMatrix3/ReadMatrix3" Application
This example demonstrates the use of buffered input and output streams.
The WriteMatrix3 and ReadMatrix3 classes perform the same tasks as the
previous two examples. The main differences are the construction of the
buffered input and output streams.
Example 6.17: The "WriteMatrix4/ReadMatrix4" Application
This is an example of reading and writing a two dimensional matrix using
Using object serialization we can easily read and write the entire array
with a single method invocation.
Example 6.18: The "WriteMatrix5/ReadMatrix5" Application
Reading and writing a two dimensional matrix using strings. This example
demonstrates character based IO. To read and write data using strings,
all data must be converted to and from their string representations. When
writing the data, delimiters must be inserted between consecutive values.
WriteMatrix5 writes the two dimensional array to a text file. Each line
contains a single value. The first two lines contains the number of rows
and columns, followed by the numbers in the matrix. ReadMatrix5 reads the
text file written by WriteMatrix5 and restores the array.
Example 6.19: The "UniversalTextViewer" Application
This example demonstrates the use of character based readers to view text
files in any character encoding. This GUI application using Swing components.
It consist of an instance of JTextArea inside an instance of JScrollPane,
which allows the text area to be scrolled vertically and horizontally.
The program expects two arguments: the text file name, and the character
encoding.
Installing fonts. To view text in a language that is different from the
language supported by your operating system, additional fonts must be installed
and configured. This process is operating system specific and language
specific. Detail
instructions are available from Sun. The following instructions are
specific to viewing simplified Chinese (ISO-GB2312) on Microsoft Windows
95/98/NT/2000 for US-English with JDK standard dsitribution for US-English.
Microsoft Internet Explorer multi-lingual package includes two TrueType
fonts for simplified Chinese, MS Song and MS Hei. Both fonts are in Unicode
encoding. Install these fonts.
Locate the font.properties file in JDK. It should be in
C:/jdk1.2.x/jre/lib/
Rename this file to a different name. Rename the file font.properties.zh
to font.properties.
Edit the font.properties.
In the lines that begin with monospaced, change the font name
\u5b8b\u4f53 to MS Song.
Comment out the two lines that begin with
fontcharset.monospaced.0 fontcharset.monospaced.1
The relevant lines in font.properties should look like this.
You are all set.
Example 6.20: The "ObjectRandomeAccessFile" Application
This example demonstrates the use of a random access file. The RandomAccessFile
does not implement the ObjectInput and the ObjectOutput interfaces. So
serialized objects can be not directly written to random access files.
In this example, we extend the RandomAccessFile to support reading and
writing serialized objects in random access files. Since serialized objects
are of variable size, for each object, we first store its size as an integer
(4 byte), then the serialized object itself. The writeObject() method first
serializes the object in a byte array, then copies the array to the file,
and the total number of bytes written to the file (including the size count)
is returned. The readObject() method reads a serialized object at the current
read/write position. It first reads the size count, then it reads the serialized
object into a byte array and deserializes the object.