by Mark Seminatore
The Java programming language, as defined by Sun Microsystems, includes a large library of standard classes. These classes are grouped into several Java packages that are guaranteed to be available in any Java implementation. A summary of the standard Java packages is shown in Table 13.1.
Unfortunately, space limitations don't allow me to explain each
method of every class in the standard Java packages. Instead,
this chapter contains an overview of the most commonly used classes
and methods. For more detailed information about individual classes,
complete descriptions of their members and methods, and their
roles in the Java class hierarchy, refer to the Visual J++ online
help.
| Note |
Visual J++ comes with very extensive online help. Not only is there complete documentation of the Visual J++ compiler, the Developer Studio, and the command-line tools, but there is a complete Java API reference, including code examples and Java object hierarchy diagrams. |
| Package | Description |
| java.lang | The core Java language classes |
| java.util | Utility classes and data structures |
| java.io | Input/output classes for streams and files |
| java.net | Classes for network operations, sockets, and URLs |
| java.awt | Abstract Windowing Toolkit classes |
| java.awt.image | Classes for managing bitmap images |
| java.awt.peer | Platform-specific AWT classes |
| java.applet | Classes for applet-specific behavior |
The java.lang package includes the classes and interfaces
that make up the core of the Java programming language. The package
also includes many individual classes encapsulating the basic
behavior of the Java language. A summary of the classes and interfaces
is shown in Tables 13.2 and 13.3.
| Interface | Description |
| Clonable | Defines an object that can be copied or cloned. |
| Runnable | Provides methods for classes that want to run as threads. |
| Class Name | Description |
| Boolean | Object wrapper for boolean values. |
| Character | Object wrapper for character values. |
| Class | Contains runtime representations of classes. |
| ClassLoader | Provides abstract behavior for loading of classes. |
| Double | Object wrapper for double values. |
| Float | Object wrapper for float values. |
| Integer | Object wrapper for integer values. |
| Long | Object wrapper for long values. |
| Math | Utility class for math intrinsics. |
| Number | The abstract parent class of all number classes (Float, Integer, and so on). |
| Object | Topmost class in Java class hierarchy. |
| Process | Provides abstract behavior for processes spawned by methods in the System class. |
| Runtime | Provides access to the Java run-time environment. |
| SecurityManager | Provides abstract behavior for implementing security policies. |
| String | Java character strings. |
| StringBuffer | Growable array of characters. |
| System | Provides platform-neutral access to system-level behavior. |
| Thread | Provides methods for controlling threads and threaded classes. |
| ThreadGroup | A group of threads. |
| Throwable | Base exception class-all thrown exception classes must derive from Throwable. |
The package includes the interface Runnable, which provides methods for classes that run as threads. Any Java program that wants to execute multiple threads must implement the Runnable interface in one or more classes. You'll find details about the Runnable interface in the discussion of the Thread class.
The classes in the java.lang package make up the core of the Java programming language. There are classes that provide system-level behavior in a platform-neutral manner, wrapper classes for Java primitives, and math intrinsic functions. Perhaps the most important classes in this package, and the most fundamental to the Java language, are Object, Thread, and Throwable.
The Object class is at the top of the entire Java inheritance hierarchy and is probably the most important class in java.lang. All classes in the Java programming environment (including your own classes) are derived either directly or indirectly from Object.
In fact, if you don't specify a parent or superclass in your class definitions, the Java compiler will assume you are inheriting from the Object class. The Object class provides some very basic methods and behaviors that all objects need, but its main purpose is to provide a focal point for the object hierarchy. The following code demonstrates both explicit and implicit inheritance in Java:
public class MyApp extends java.applet.Applet // explicit superclass
{
...
}
public class MotorBoat // Java assumes "extends Object"
{
...
}
Because Java supports only single inheritance, each subclass can trace a single path back to the Object class. This restriction prevents the ambiguous situation that can occur in SmallTalk or C++ whereby a class, through multiple inheritance, finds itself with two (or more!) copies of the Object class.
You need to understand the significance of the Object class and the single inheritance model of Java. These concepts play a key role in how you will design and develop your own Java applets.
java.lang also includes several object wrapper classes. These classes provide an encapsulation of the intrinsic data type primitives of the Java language. By design, the basic data types in Java (boolean, char, int, long, float, and double) are not classes. This philosophy is in contrast to other "purer" object-oriented languages such as SmallTalk. Rather, for reasons of efficiency, Java's basic data types are implemented as special native data types.
Because of this design compromise, in Java you cannot substitute a primitive data type for an instance of the Object class. For example, you cannot build a list of int values using the Vector class.
The following code shows how the Java primitive data types are not interchangeable with instances of the Object class:
// define my new list class
public class MyList // possibly derived from class Vector
{
public AddToList(Object AnObject) {...}
}
// some object to add to the list
public class House
{
...
}
List.AddToList(new House); // ok
List.AddToList(14); // illegal, 14 is not an object
Obviously, it is desirable to have a workaround for situations in which Java primitives must coexist with instances of the Object class. The object wrapper classes provide a solution by allowing you to convert primitive data types to an equivalent object class and then back again at some later time. The following code demonstrates the use of the Integer object wrapper class:
// create an Integer object Integer anIntObject = new Integer(14); // get the primitive value back int anInt = anIntObject.intValue(); List.AddToList(new House); // ok! List.AddToList(14); // illegal, 14 is not an object List.AddToList(new Integer(14)); // ok, object passed
The String class provides a means of storing and manipulating strings of characters. In Java, strings are not just simple arrays of characters ending in '\0' as they are in C or C++. Rather, all strings in Java are instances of the String class. Because String objects are real objects (inheriting ultimately from class Object), they have methods that make testing the length of a string, adding and deleting individual characters, very easy.
The System class provides a platform-neutral way to access to Java's system-level behavior. Important tasks such as reading and writing to standard input and output are found in this class. Methods are also provided for accessing the system garbage collector, loading dynamic link libraries, getting and setting system properties, accessing system environment variables, getting the current system time, and other low-level system activities. The following code provides some sample usages of the System class:
// tell the garbage collector to "step back" and clean up
System.gc();
// retrieve system properties
System.initProperties();
System.getProperty("java.version");
// access standard output stream
System.out
// access standard input stream
System.in
// access standard error stream
System.err
// retrieve a reference to the current security manager object
System.getSecurityManager();
// retrieve current GMT time in milliseconds since Jan. 1 1970
long aTimeCount = System.getTimeMillis();
Another class in this package is the class Thread. This class provides methods for managing threads and classes that run in threads. A class that implements the interface Runnable will typically instantiate one or more instances of class Thread. The instances of class Thread may then be used to control execution of these threads.
The last class in this package is the class Throwable. This class is a generic exception class from which all exceptions and thrown objects must inherit. That is to say, any exception that is thrown by an application must derive (ultimately) from class Throwable (usually by way of class Exception). The Throwable class encapsulates common exception behaviors such as printing stack traces and returning text messages describing the exception.
The java.util package contains numerous utility classes
and interfaces. These classes provide behavior such as random
number generation, system properties, and common data structures.
A summary of the classes and interfaces is shown in Tables 13.4
and 13.5.
| Interface | Description |
| Enumeration | Provides methods for enumerating through sets of values. |
| Observer | Provides methods for classes to observe descendants of the Observable class. |
| Class Name | Description |
| BitSet | A set of bits. |
| Date | Provides methods to retrieve the current system date, and generate and decode dates. |
| Dictionary | An abstract class to manage key value pairs. |
| Hashtable | A subclass of Dictionary that implements a hash table. |
| Observable | An abstract class defining observable objects. |
| Properties | A persistent hash table for getting/setting properties of a class or the Java system. |
| Random | Provides methods to generate psuedorandom numbers. |
| Stack | A last-in, first-out (LIFO) stack. |
| StringTokenizer | Provides methods for parsing strings into a sequence of tokens. |
| Vector | A growable list of objects. |
The interfaces in java.util are provided to allow other Java classes to interact with each other. The Enumeration interface provides methods that abstract the behavior of retrieving items (actually Objects) from a list in sequential order. The Observer interface abstracts the behavior that allows classes to interact with descendants of Observable classes. The Observer and Observable relationship supports the Model-View methodology. Space does not permit a discussion of the Model-View philosophy here, but a good book on object-oriented design concepts should contain an explanation of the concept.
The interfaces defined by the package include Enumeration and Observer. The Enumeration interface provides methods for enumerating (or counting through) sets of values. Note that the set is consumed or destroyed by use, and its values may be used only once. The following code creates a class that can enumerate a list of Objects:
// a class which can enumerate sets of values
public class MyEnumerator implements Enumeration // extends Object implied
{
...
}
The Observer interface provides methods for allowing classes to observe Observable objects. This interface is used in the Java implementation of the model-view paradigm. The following code derives an Observer class:
public class MyObserver implements Observer // extends Object implied
{
...
}
The classes in java.util provide auxiliary features of the Java language. They include classes that implement basic data structures such as Dictionary, Hashtable, Stack, and Vector. The Date class provides methods for retrieving and manipulating dates. Lastly, there are classes such as Properties and StringTokenizer. The former provides methods to store and retrieve property values in a manner similar to the Windows INI files. The latter processes data from a string into a sequence of "tokens."
The BitSet class provides a hardware independent abstraction of, and a means for, storing and manipulating lists of bits. Methods are provided for getting and setting individual bits, logically comparing and/or combining two sets of bits, and displaying a string representation of the set. The list is implemented so that the list may grow as needed to store more bits. The following code demonstrates the usage of the BitSet class:
// create an empty set of bits BitSet bitset = new BitSet(); // create a set of 100 bits BitSet bitset = new BitSet(100); // set bit 23 bitset.set(23); // clear bit 23 bitset.clear(23); // get bit 23 boolean bool = bitset.get(23);
The Date class provides a wrapper for a date. The class implements a platform independent way of manipulating and parsing dates. The following code shows how the Date class might be used:
// Print today's date
Date d = new Date();
System.out.println("today = " + d);
// Find out what day corresponds to a particular date:
Date d = new Date(63, 0, 16); // January 16, 1963
System.out.println("Day of the week: " + d.getDay());
The Dictionary and Hashtable classes provide implementations of the common data structures of the same name. A Dictionary can typically store and retrieve key-value pairs from a table. Both the key and the value may be objects. The Dictionary class in this package is actually an abstract class used simply as a base class for Hashtable:
// Creates a hashtable of numbers. Uses the names of the numbers as keys.
Hashtable numbers = new Hashtable();
// add some entries in the table
numbers.put("one", new Integer(1));
numbers.put("two", new Integer(2));
numbers.put("three", new Integer(3));
// Retrieve a number. Note use of integer object wrapper class
Integer aNumber = (Integer)numbers.get("two");
if (aNumber != null)
{
System.out.println("two = " + aNumber);
}
The Properties class inherits from the Hashtable class. It provides behavior for setting and retrieving persistent properties (as object key-value pairs) of the system or of a particular class. The properties may be saved or loaded to or from a stream. If a particular property is not found, an optional list of default properties is searched. The latter provides a means of nesting properties within properties. The following code demonstrates the Properties class:
// create property list and define a default set of properties
Properties props = new Properties(defaultProps);
// load the persistent properties
InputStream s = new FileInputStream("properties.dat");
props.load(s);
// retrieve a value
String result = props.get("filepath");
The Random class provides various utilities for generating pseudorandom numbers. The class can generate random numbers of various lengths and types including ints, longs, floats, and doubles. The Random class can also generate real numbers approximating a Gaussian probability distribution. The following code shows how the Random class could be used:
// create an instance using the system timer as a seed. Random rand = new Random(); // create an instance with a seed value. Random rand = new Random(12345678); // generate some random numbers int value = rand.nextInt(); long value = rand.nextLong(); float value = rand.nextFloat(); double value = rand.nextDouble(); double value = rand.nextGaussian();
The Stack class implements a last-in-first-out (LIFO) stack or queue. The class is derived from the Vector class and can hold arbitrary lists of Objects. Methods include the typical push(), pop(), and peek() features of a stack data structure. The following code demonstrates the Stack class:
// instantiate a Stack object
Stack stack = new Stack();
// fill up our stack
stack.push("Hello");
stack.push("World!");
// results: World! Hello
System.out.println(stack.pop() + " " + stack.pop());
One of the more significant classes in this package is the StringTokenizer class. This class provides utilities for splitting or parsing strings into sequences of tokens. The class is very general and is useful in many applications. The delimiter defaults to common white-space characters such as tab, space, or newline. The delimiter may be redefined at creation or on a per-token basis. The following code shows a sample of how the StringTokenizer class might be used:
String s = "this is a test";
StringTokenizer st = new StringTokenizer(s);
while (st.hasMoreTokens())
{
println(st.nextToken());
}
// The following is printed on the console
this
is
a
test
The final class in this package is the Vector class. This class provides an implementation of a "growable" array or list of objects. The list will expand automatically, as required, to contain all of the objects in the list. If no arguments are specified in the constructor, the Vector is instantiated with default values for the capacity and capacityIncrement. The capacity and capacityIncrement members specify the current size of the Vector and how much to grow the Vector, respectively. Methods for common list operations such as addElement(), removeElement(), indexOf(), contains(), and isEmpty() are provided:
// instantiate a vector object with default capacity and capacity increment Vector vector = new Vector(); // instantiate a vector object with capacity of 10 // and the default capacity increment Vector vector = new Vector(10); // instantiate a vector object with a capacity of 10 // and a capacity increment of 10 Vector vector = new Vector(10,10); // add an object to our list vector.addElement(new Integer(14));
The java.io package provides a number of classes for
input and output to streams and files. The classes rely on several
interfaces that java.io defines including DataInput,
DataOutput, and FilenameFilter. The classes
in java.io can be logically separated into two categories:
classes that read from streams and classes that write to streams.
The following discussion maintains this structure. A summary of
the classes and interfaces is shown in Tables 13.6 and 13.7.
| Interface | Description |
| DataInput | Provides methods for reading input streams in a platform-independent manner |
| DataOutput | Provides methods for writing output streams in a platform-independent manner |
| FilenameFilter | Provides methods for filtering filenames |
| Class Name | Description |
| BufferedInputStream | An input stream with buffering |
| BufferedOutputStream | An output stream with buffering |
| ByteArrayInputStream | An input stream reading from an array of bytes |
| ByteArrayOutputStream | An output stream writing to an array of bytes |
| DataInputStream | An input stream that can read Java primitive data types |
| File | A class that encapsulates a file on the host system |
| FileInputStream | An input stream reading from a file |
| FileOutputStream | An output stream writing to a file |
| FilterInputStream | An abstract class that allows processing of stream bytes as they are read |
| FilterOutputStream | An abstract class that allows processing of stream bytes as they are written |
| InputStream | An abstract class that represents reading a stream of bytes-the parent of all input streams |
| LineNumberInputStream | An input stream that counts line numbers |
| OutputStream | An abstract class that represents writing a stream of bytes-the parent of all output streams |
| PipedInputStream | A piped input stream-must be connected to a PipedOutputStream |
| PipedOutputStream | A piped output stream-must be connected to a PipedInputStream |
| PrintStream | An output stream used for printing |
| PushbackInputStream | An input stream that can unwrite the last byte read |
| RandomAccessFile | Provides methods for random-access manipulation of a file |
| SequenceInputStream | Links several input streams into a single input stream |
| StreamTokenizer | Parses data from an input stream into a sequence of tokens |
| StringBufferInputStream | An input stream reading from a StringBuffer object |
The DataInput and DataOutput interfaces in java.io provide methods for reading and writing data to streams. They are designed to do this in a completely machine-independent manner. The FilenameFilter interface provides just a single method. The interface provides a means for filtering a list of filenames from a list.
The DataInput interface provides methods for reading data from machine-independent typed input streams. Some of the methods that the DataInput interface implements are readBoolean(), readByte(), readChar(), readInt(), and readFloat(). These methods hide the details of the underlying system hardware and allow Java programs to treat all streams identically, regardless of whether they are running under UNIX, Windows, or the MacOS. The following code demonstrates a class that implements the DataInput interface:
// a class that can read from input streams
public class MyReader implements DataInput // implied extends Object
{
...
}
Because Java programs also need to write to streams, the DataOutput interface is provided for writing data to machine-independent typed output streams. The methods that this interface includes are analogous to those provided by DataInput. Examples are writeBoolean(), writeByte(), and writeFloat(). These interfaces provide much of the underlying behavior common to the classes in this package. Nearly every class in java.io implements either the DataInput or DataOutput interface.
The following code demonstrates a class that implements the DataOutput interface:
// a class that can write to output streams
public class MyWriter implements DataOutput // implied extends Object
{
...
}
The FilenameFilter interface is a very simple abstraction that provides methods for filtering filenames. Only one method is defined: accept(). The method takes two parameters, dir and name, that represent the directory location and filename, respectively. The method returns a boolean value that indicates whether the given file should be included in a file list. The following code shows how simply the FilenameFilter interface could be implemented:
// essentially the entire interface!
public interface FilenameFilter
{
boolean accept(File dir, String name);
}
The input stream classes in java.io provide methods and behavior for reading streams of data. Classes such as ByteArrayInputStream, StringBufferInputStream, and FileInputStream encapsulate different sources of data. Other classes such as DataInputStream and BufferedInputStream define different methods of reading and interpreting data. Still other classes such as StreamTokenizer and LineNumberInputStream provide methods for processing the data as it is read.
The classes InputStream and OutputStream are the parent classes for the rest of the java.io library. InputStream and OutputStream are abstract classes that define the basic ways that a stream of bytes moves from a source to a destination. Because the classes are abstract classes (that is, they cannot be instantiated), they know nothing about the source of the bytes or the details of transporting the data from source to destination. I will cover the input streams first and then the output streams.
The class ByteArrayInputStream is perhaps the simplest class in java.io. It is used to create an input stream from an array of bytes. This class essentially allows a Java program to treat an array like any other data source. The following shows two ways to construct a byte array stream:
// create a byte array - note an array is _not_ an object byte[] aBuffer = new byte[256]; // fill the byte array somehow fileByteArray(aBuffer); // instantiate a byte stream ByteArrayIntputStream byteStream = new ByteArrayInputStream(aBuffer); // instantiate another byte stream ByteArrayIntputStream byteStream = new ByteArrayInputStream(aBuffer, 10,100);
A class that reads from byteStream will "see" a stream of 256 bytes. ByteArrayInputStreams do not implement any additional methods beyond those inherited from InputStream.
Another class that is very similar to ByteArrayInputStream is StringBufferInputStream. This class is identical in every way to ByteArrayInputStream except that rather than being constructed from a byte array, it accepts a String object. The following shows one possible usage of the StringBufferInputStream class:
// define a string object String aString = "A long time ago, in a galaxy far far away..."; // construct our stream StringBufferInputStream aStream = new StringbufferInputStream(aString);
A FileInputStream object attaches a stream to a file
in the underlying file system. As such, it is perhaps one of the
most commonly used stream classes in Java applications. File stream
objects provide a simple streamlike way of reading data from files.
| Note |
Java applets may or may not be allowed to open file streams depending upon the security level of the user's browser. In general, applets should be designed so that file I/O is not required. Information may be stored on servers. Java applications do not have this limitation. |
The following code shows how to use the FileInputStream class:
// instantiate a file stream object
FileInputStream aStream = new FileInputStream("\public\stuff\filename");
// create a buffer to hold data
byte[] aBuffer = new byte[512];
// read some data
String aString = aStream.read(aBuffer);
// get the file descriptor
int aFileDescriptor = aStream.getFD();
| Note |
For most purposes, a convenient way to view a file is as just another type of stream. This philosophy developed under UNIX and was later expanded in C++. Java encourages the programmer to extend the idea even further--to include almost every type of I/O, even URLs! |
The FilterInputStream class is derived from InputStream and defines some very unique behaviors. FilterInputStreams are constructed with another stream as a parameter. The FilterInputStream object contains the passed stream. Any method calls to FilterInputStream are passed down to this stream. This nesting relationship of streams is a basic feature of FilterInputStreams, and as you will see, it encourages a rather unique way of viewing streams.
Descendants of FilterInputStream may, of course, define more complicated methods for determining how to handle bytes as they flow to and from the nested stream. How is this behavior useful? By reinforcing a layered view of streams, it encapsulates, in a very intuitive way, the essential behavior of data filters or translators. In the following code sample a simple class translates data read from a file:
// a class that inherits FilterInputStream behavior and translates data
public class MyTranslator extends FilterInputStream
{
...
}
// setup our translator object
MyTranslator myTrans = new MyTranslator(new FileInputStream("\public\foo.txt"));
// start reading translated data!
myTrans.read(aBuffer);
The class above could provide a read method that translates carriage returns into carriage-return line-feed pairs as data is read from the owned FileInputStream. The next few classes, which are also descendants of FilterInputStream, give a better example of the usefulness of FilterInputStreams.
One of the more valuable stream classes in java.io is the BufferedInputStream. The class is a direct descendant of FilterInputStream. This class implements all of the behavior of InputStreams while providing the additional capability of buffering or caching data for future reading.
This type of behavior is very useful when data is most efficiently read from a stream in finite blocks or "chunks," for example, disk files or network packets that are often most efficiently read 512 or 1024 bytes at a time. Consider the following examples:
// create a buffered file input stream with default buffer size
BufferedInputStream = new BufferedInputStream(new FileInputStream("foo.txt"));
// create a buffered file input stream with a specified buffer size
BufferedInputStream = new BufferedInputStream(new FileInputStream("foo.txt", 512));
A BufferedInputStream is typically used in conjunction with another stream such as FileInputStream. The code above demonstrates one such usage and exemplifies the nested stream behavior that FilterInputStream provides.
The class DataInputStream derives from FilterInputStream and implements the interface DataInput. Therefore, DataInputStreams can read any of the primitive data types from a stream. This feature is essential for writing portable Java applets. Consider the problem of reading in complex data from streams such as FileInputStream. If you wanted to read, for example, a floating-point number, you would ordinarily need to understand the details of how that number was represented in binary form in the file system.
In order to eliminate this difficult situation from Java programs, Sun provided the DataInputStream class. Because DataInputStream implements the DataInput interface, it knows how to read all the primitive Java data types from a stream. By deriving from the FilterInputStream class, it also inherits the ability to contain and read from another stream. This means that a DataInputStream can be used to postprocess bytes of data read from another input stream, converting them into Java data types such as int and float. Because reading integers and floating-point numbers from a stream or file is so common, you will find many instances where you will want to derive your own classes from DataInputStream. Perhaps the following code, which shows DataInputStream in action, will help to clarify things:
// construct a data input stream
DataInputStream aStream = new DataInputStream(new FileInputStream("foo.txt));
// various methods for reading data primitives
boolean aValue = aStream.readBoolean();
byte aValue = aStream.readByte();
int aValue = aStream.readUnsignedByte();
short aValue = aStream.readShort();
int aValue = aStream.readUnsignedShort();
char aValue = aStream.readChar();
int aValue = aStream.readInt();
long aValue = aStream.readLong();
float aValue = aStream.readFloat();
double aValue = aStream.readDouble();
// read an ascii string terminated by carriage-return or line-feed
String aString = aStream.readLine();
// read a Unicode string
String aString = aStream.readUTF();
In this code, you might have noticed that the return types of some of the integer read methods-readUnsignedByte() and readUnsignedShort(), for example-appear to be incorrect. In fact, they are not. Because integer types in Java are signed, an unsigned integer value cannot, for example, fit in an integer variable. Rather, the return value is promoted to the next larger integer size. Therefore, unsigned shorts and unsigned bytes require an integer variable.
The LineNumberInputStream class is another useful child of FilterInputStream. This class keeps track of line numbers as its stream data is processed. This type of behavior is very useful in subclasses that implement an editor, in a compiler, or perhaps even in a debugger. The most useful method provided by this class is getLineNumber(). The following code shows how a LineNumberInputStream could be used:
// create our stream and associate it with a file
LineNumberInputStream aLineStream;
aLineStream = new LineNumberInputStream(new FileInputStream("foo.txt"));
// construct a useful working view of the stream
DataInputStream aStream = new DataInputStream(aLineStream);
// read some data - ultimately from a file
int aValue = aStream.readInt();
// show the current line number
System.out.println("Just read line" + aLineStream.getLineNumber());
The class PushbackInputStream is a special purpose class that derives from FilterInputStream-as do many other classes in java.io. This class is commonly used in parsers that often need to "push back" or unread a single piece of the input after reading it. Recursive descent parsers (the easiest to hand-code) frequently require the use of this technique. As you might expect, one of the new methods provided by the class is called unread().
The SequenceInputStream class can be used to glue together two separate streams. It allows the resulting stream to be passed along to another class or method that was designed to handle only a single stream. This programming technique is easier than calling the method once for each stream. Perhaps more useful is the ability to link together an arbitrary number of streams, as shown in the following code sample:
// construct a list to hold our streams Vector aVector = new Vector(); // call a method which populates our list aClass.fillTheVectorWithStreams(v); // presto chango and voila! The new stream behaves as if it is a single entity DataInputStream aStream = new SequenceInputStream(v.elements());
The preceding paragraphs covered nearly all of the input streams in java.io; it is time to discuss the flip side of I/O, output streams. In almost every case, each of the input streams has a companion class that defines similar behavior for output streams.
At the top of the output stream hierarchy is the abstract class OutputStream. This class defines the basic ways that a source writes a stream of data. As with the input streams, the specifics of data production, transport, and eventual storage are unimportant. Methods common to all output streams are write(), flush(), and close(). As you would expect, the write() method can be expressed in several polymorphic forms.
The ByteArrayOutputStream class directs its output to an internal array of bytes. The data in the buffer can then be extracted using one of several methods. The class also provides methods for determining the size of the internal byte array and for clearing the array. These methods are size() and reset(), respectively. The data can be retrieved from the byte array using methods such as toByteArray() and toString(), as shown here:
// construct our stream ByteArrayOutputStream aStream = new ByteArrayOutputStream(); // fill up the internal byte array writeStuffToStream(aStream); // extract some data to an external byte array byte[] aBuffer = aStream.toByteArray(); String aString = aStream.toString();
The class FileOutputStream is a direct analogue to FileInputStream. As they do with FileInputStream, Java applets may experience problems attempting to access files on the host system. Java applications do not have this problem. FileOutputStreams can be constructed from a string object representing a filename or from a previously allocated file descriptor.
The FilterOutputStream provides nested output streams. Like FilterInputStream, this class serves as an anchor for several more specific subclasses. Although data written through a FilterOutputStream is passed along without modification, the subclasses all perform some sort of additional processing.
BufferedOutputStream that derives from FilterOutputStream provides, as you might guess, buffered writes of data streams. Data passed through a buffered output stream is collected in an internal buffer until a specified threshold is reached. At this point, the data is "written" to the output. In practice, the output is actually written to the nested stream. As with buffering of inputs, this technique is commonly the most efficient means of writing data to physical devices such as the file system or the network.
The DataOutputStream class implements the DataOutput interface, which enables DataOutputStream to write out any of the native Java primitives. This capability is useful for storing application-specific data.
The PrintStream class is used mainly by the System class to provide Java applets and applications with a system-independent way of reading from and writing to the console window. The PrintStream members of the System class are System.err and System.out. On most host systems System.err and System.out are attached to the standard error and standard output, respectively. PrintStream does not have an input analogue. Using System.out is straightforward:
// demonstrate use of the PrintStream class System.out.print(...); System.out.println(...);
The classes PipedInputStream and PipedOutputStream together support UNIX-style pipe connections, which are usually used to set up a safe means of communication between two threads. One thread would write to a PipedOutputStream while another would read from a PipedInputStream. If each thread has both a PipedInputStream and a PipedOutputStream, then two-way communication between threads is possible.
The File class serves as a platform-neutral abstraction of a file. When it's constructed, a File object is passed a physical filename. The File class has methods that return the type, status, and various properties of entities in the file system.
A few other classes in java.io, such as RandomAccessFile and StreamTokenizer, are similar to classes already discussed. For additional information on these classes, refer to the Java API description in the Visual J++ online help.
The java.net package is quite possibly one of the most exciting packages in the Java class library. It provides a number of interfaces and classes that perform network operations and includes support for UNIX-style TCP/IP sockets and protocols such as FTP and HTTP.
A summary of the classes and interfaces in java.net is shown in Tables 13.8 and 13.9. The rest of this discussion relates to java.net classes only, because these interfaces are not commonly used in typical Java applets.
java.net supports three types of network communication:
AppletContext.showDocument(), URL.openStream(),
and the UNIX-style Socket classes. By far the easiest
way to jump into network programming is to use the showDocument()
method. But first you need to understand uniform resource locators
(URLs) and the URL class.
| Interface | Description |
| ContentHandlerFactory | Provides methods for creating ContentHandler objects. |
| SocketImplFactory | Provides methods for creating socket implementations. |
| URLStreamHandlerFactory | Provides methods for creating URLStreamHandler objects. |
| Class Name | Description |
| ContentHandler | Provides abstract behavior for processing data from a URL connection-uses MIME types to construct local objects. |
| InetAddress | Encapsulation of an Internet host. |
| ServerSocket | Encapsulation of a server socket. |
| Socket | Encapsulation of a client socket. |
| SocketImpl | Abstract class for particular socket implementations. |
| URL | Encapsulation of a uniform resource locator. |
| URLConnection | Provides abstract behavior for a socket that processes Web protocols such as HTTP and FTP. |
| URLStreamHandler | Abstract class for creating and handling Web streams such as HTTP and FTP. |
The classes in java.net make network programming in Java relatively painless! Remember, however, that for security reasons Java applets are not (but standalone Java applications are) allowed to read from or write to the hard drive on the host machine. Reading and writing data from the server on which the Java applet resides are allowed. The specific limitations placed on Java applets may sometimes be controlled through settings in the browser.
The URL class encapsulates the notion of a uniform resource locator. If you are familiar with Web pages and the Internet, you already know about URLs. They are the address strings that so often appear as hot links on Web pages. Some examples are http://www.foo.edu and ftp://ftp.foo.com. An instance of the class URL is needed to create links to Web content in a Java applet.
The constructor for class URL comes in several forms. As you might expect, the different forms are all useful variations on the same theme. The first form is the most generic, allowing you to specify explicitly each component of a URL. The parameters include the protocol, the host address, the port, and a filename. The set of valid protocols includes any that the browser supports, including HTTP, FTP, Gopher, file. The host address is of the form www.foo.edu. The port number must be a valid port for the given protocol. For example, Web servers usually listen to port 80 for HTTP requests. The filename represents the file or pathname on the host system.
The second form is identical to the first form except that it leaves out the port number. The third constructor allows a location to be specified as a combination of a base path and a relative path. The base path can either be the results of a call to getDocumentBase() for the URL of the current HTML file or a call to getCodeBase() for the URL of the Java applet file or an arbitrary URL. The relative path is passed in as a String object. It is concatenated to the directory specified by the base path. Note, however, that if the String object representing the relative path is actually an absolute address, the absolute address will be used by itself.
The last form of the constructor takes just a single string object as a parameter. The string must specify the complete URL address. Beware that if you pass a String that is not a properly formed URL address, you will cause an Exception to be thrown that you must handle. Handling a thrown exception requires a try...catch clause. The following code shows the various ways to create a URL object:
// create a URL given protocol, host, port, and filename // protocol can be: http, ftp, gopher, file, etc. // host might be: www.foo.com, etc. // port is 80 for http, etc. URL(String, String, int, String); // create a URL given protocol, host and filename URL(String, String, String); // create a URL using a base path and a relative path // the URL object represents the base location // URL(URL, String); // create a URL from a complete URL string URL(String);
The code below shows how you might go about creating a URL from a String object. Note the use of the try...catch exception handling in the code:
// create a string object
String aString = "http://www.foo.com/";
// instantiate a URL object
try
{
aURL = new URL(aString);
}
catch( MalformedURLException e)
{
System.out.println("Error in URL: " + aURL);
}
Once you have a valid instance of class URL, you can ask the browser to display the contents of the new page using the following code. The first part of the code is actually a call to a method of class Applet. This method returns an AppletContext object that is an encapsulation of the browser currently running the Java applet. The second part of the code is a call to a method of AppletContext, which tells the browser to display the document or location represented by our URL object. The following code uses showDocument() to display the contents of a URL location:
// tell browser to display contents of new URL location getAppletContext().showDocument(aURL);
The class InetAddress encapsulates the properties of an Internet address. The constructor for this class has two forms. The first form creates an empty Internet address object. The internal fields of the object are filled in by a call to the accept() method of class Socket.
The InetAddress class is most often used as an "owned" object by other classes such as Socket(). However, you may find uses for it in your own applets. Given a properly constructed Internet object, you can call methods such as getHostName() and getAddress() that return strings representing the Internet address and the raw Internet address. The following code shows the two forms of the constructor for InetAddress:
// parameters provided by Socket.accept() InetAddress(); // hostname and IP address () specified InetAddress(String, byte[]);
Network streams are useful for the times when you want to give your Java applet more sophisticated network capabilities. Rather than simply telling the browser to display the contents of Web pages, you might want your applet to somehow process the contents of a file. For example, you might have a set of files on a server that represents some useful data; you want the user to select one of the files and then have your applet display a visual representation of the data. Perhaps you need to display contour plots or a graph.
The URL class provides a method-openStream()-that helps you develop these types of applications. The openStream() method allows you to work with a network file as if it were a stream. This method opens a network connection based upon the contents of the URL object and then returns an instance of the class InputStream (discussed in java.io), as shown here:
// create a network stream from a valid URL InputStream aStream = aURL.openStream();
Note that the preceding code, and any code that makes I/O calls, should be wrapped with code to handle any thrown exceptions. Given the instance of InputStream, you can then read data from the stream using any of the stream methods discussed earlier. For example, the following code defines a new class MyStreamClass that is essentially the same as DataInputStream but perhaps defines some additional processing of the data. You can nest a buffered input stream within the new class for better efficiency. Finally, you can use the methods provided by the DataInput interface to read any type of data from this stream. The following code assumes a network stream, aStream, has already been opened:
// define a new class
public class MyStreamClass extends FilterInputStream implements DataInput
{
...
}
// create a buffered version of our network stream
MyStreamClass myStream = new MyStreamClass(new BufferedInputStream(aStream));
// read some data from network file
String aString = myStream.readLine();
while(aString != NULL)
{
System.out.println(aString);
aString = myStream.readLine();
}
The preceding code is an overly simplistic example of how network streams might be used. In a real application large amounts of data might be processed, in which case a better coding technique is to create a separate execution thread to process the data stream. This type of processing prevents the network activities, which might take some time to complete, from monopolizing all of the applet's execution time. Also note that the preceding code does not include any way to handle I/O exceptions. A real Java applet/application code needs a graceful way to handle I/O exceptions.
The Socket and ServerSocket classes provide Java programs with a programmatic interface nearly identical to standard UNIX-style sockets. The following code constructs a new socket object:
// create a socket object
Socket aSocket = new Socket("hostname", 80);
The form of the Socket constructor used here takes a hostname and a port number as parameters. Having created a client-side socket connection, you can now ask the Socket object to provide input and/or output streams for communication through the socket. The methods getInputStream() and getOutputStream() return instances of InputStream and OutputStream classes, respectively. These streams can be nested inside buffered streams and data input streams to provide a simple means of communication across a network connection. The following sample code retrieves input and output streams:
// returns instance of InputStream aSocket.getInputStream(); // returns instance of OutputStream aSocket.getOutputStream();
After the program finishes with a Socket, you should be sure to close the connection:
// close the socket aSocket.close();
You can use server sockets similarly. The main difference is the additional requirement that the ServerSocket must listen to and accept a connection request. The constructor for ServerSocket takes a single parameter representing the TCP port number. After instantiating the server socket, the socket is "bound" to the given port:
// create a server socket listening to port 8000 ServerSocket aServer = new ServerSocket(8000); // listen to and accept any connection requests on port 8000 aServer.accept();
Once the server socket is created, it automatically listens to the TCP port for a connection request. When a request is received, the server socket accepts the request that establishes a link between the client socket and the server socket. As with client-side sockets, the server socket has methods for retrieving input and output streams. When you're finished with the server socket, it must be closed:
// close off the server socket connection aServer.close();
Both the Socket and ServerSocket class methods
eventually map down to calls to the underlying operating system.
In this case calls are made to the TCP/IP stack; Windows accomplishes
this task via the WINSOCK.DLL library.
| Note |
Socket programming is a large and diverse topic, and I cannot go into every nuance here. Many good books on UNIX socket programming are available. |
The java.awt package includes the classes and interfaces that make up the Java Abstract Windowing Toolkit (AWT). The AWT was designed to provide a platform-neutral abstraction of a graphical user interface or GUI. It is hard to overstate the importance of the AWT to the Java programming language. Whether you are writing embedded Java applets or standalone Java applications, avoiding the AWT is nearly impossible.
The appeal of the AWT is that it very effectively hides the implementation details of different windowed operating systems. Consequently, a single Java program written using the AWT has the ability to run with a consistent user interface under any operating system for which a Java virtual machine exists. Currently, that category includes systems such as Windows 95/NT, the MacOS, UNIX/X Windows, or NeXTSTEP.
The AWT as defined by Sun Microsystems includes classes that provide support for a complete set of user interface components including buttons and check boxes, static text fields, menus, windows, scrollbars, and dialogs. These basic visual objects are called components; in fact, they are all derived from a common base class called Component. The class Component forms the root of the AWT class hierarchy and provides the basic functionality common to all AWT objects.
The AWT also introduces the concept of the user interface "container." Objects of this class and its descendants are capable of containing any of the various AWT components. Because the Container class is derived from the Component class, Container objects can also hold other Container objects. This nesting of components and containers creates an environment that allows complex interaction within a coherent recognizable structure. The most important container in the AWT is the Panel, which is a container-component having both a visual representation and the ability to contain other components.
The AWT also provides a complete event-driven system for managing and responding to user events. The event model is very robust and provides the primary basis for communication between various components of the AWT within an application. If you are at all familiar with other GUI event managers, you should have no trouble mastering AWT events.
One of the thornier problems that the AWT elegantly solves is how to manage the layout of graphical screens across so many disparate platforms. Indeed, a wide range of screen resolutions, color depths, and so on may exist within each platform. The model that the AWT uses is rather loosely based on that of the UNIX X-Windows system developed at MIT to solve similar hardware-compatibility problems. Rather than defining component layouts in terms of a fixed, screen-based, x,y grid, layouts are specified in more general terms, such as top or bottom, northeast corner, and so on. This model allows each implementation of the AWT to mask the underlying hardware differences while still providing flexible screen designs.
A summary of the classes and interfaces in java.awt is
shown in Tables 13.10 and 13.11. The AWT, although it may sound
like an optional, albeit very useful, part of Java is actually
very tightly woven into every Java program. For example, the Applet
class that forms the basis of every browser-embedded Java program
is derived directly from class Panel, which is an integral
part of the AWT.
| Interface | Description |
| LayoutManager | Provides methods for laying out containers. |
| MenuContainer | Provides methods for menu-related containers. |
| Class Name | Description |
| BorderLayout | A layout manager for arranging components around the borders of a container. |
| Button | A pushbutton object. |
| Canvas | Used for drawing graphics. |
| CardLayout | A layout manager for slideshow-like layouts. |
| Checkbox | A check box object. |
| CheckboxGroup | A group of radio buttons. |
| CheckboxMenuItem | A checkable menu item. |
| Choice | A pop-up menu. |
| Color | Encapsulates a color representation. |
| Component | The abstract base class for all components. |
| Container | Provides abstract behavior for a component that can contain other components or containers. |
| Dialog | A dialog box window. |
| Dimension | An object encapsulating a width and height. |
| Event | An object encapsulating system or user events. |
| FileDialog | A dialog box for selecting filenames from a list. |
| FlowLayout | A layout manager that performs a left-to-right and top-to-bottom layout. |
| Font | Encapsulates a font representation. |
| FontMetrics | Holds information about a Font object. |
| Frame | A top-level window. |
| Graphics | Encapsulates behavior for drawing graphic shapes and objects. |
| GridBagConstraints | Provides class constants for the GridBagLayout manager. |
| GridBagLayout | A layout manager that aligns Components using user-supplied constraints. |
| GridLayout | A layout manager using rows and columns. |
| Image | Encapsulation of a bitmap image. |
| Insets | A layout manager helper class-encapsulates Component distances from border of window frame. |
| Label | A text label object. |
| List | A list box object. |
| MediaTracker | Provides a means to track the status of media objects. |
| Menu | A menu containing menu items. |
| MenuBar | A menu bar containing Menu objects. |
| MenuComponent | The parent class of all menu objects. |
| MenuItem | A single menu item. |
| Panel | A visible Container object. |
| Point | Encapsulates x and y coordinates. |
| Polygon | Encapsulates a set of points defining a polygon shape. |
| Rectangle | Encapsulates x, y, width, and height of a rectangle. |
| Scrollbar | A scrollbar object. |
| TextArea | A multiline edit box. |
| TextComponent | The parent class of all editable text components. |
| TextField | A single-line edit box. |
| Toolkit | Provides behavior for binding abstract AWT classes to implementation level objects. |
| Window | A top-level window-parent of Frame and Dialog. |
The LayoutManager interface is used as a "mix in" to provide layout capabilities to the layout managers that are derived from class Object. Layout managers are discussed in depth later in this chapter. If you wanted to create your own layout, you would derive a new class from Object that implements the LayoutManager interface. The new layout could then be used in place of one of the standard layout managers as shown here:
// define a new layout manager
public class MyLayout implements LayoutManager // extends Object implied
{
...
}
// change the layout manager
setLayout(new MyLayout());
The component classes in java.awt provide a complete set of platform-independent user interface objects. There are classes for displaying pushbuttons, text boxes, scrollbars, windows, and dialog boxes. Several of the classes, including descendants of Container such as Window, Frame, Dialog, and Applet, may contain other components.
The abstract class Component is derived directly from the Object class. A component represents any object that has a visual representation in the AWT. Components make up the fundamental units with which the majority of AWT classes work. Because Component is an abstract class, it cannot be instantiated; therefore, it serves only as base class for other objects.
As discussed earlier, the AWT introduces the concept of a container. The abstract class Container is a child of class Component. As defined, a Container may add or contain any type of object derived from class Component, meaning that containers can contain other containers. Like Component, the Container class is abstract and therefore cannot be instantiated.
The Color class encapsulates the concept of a color. It does so in a most general way by defining a 24-bit RGB color space with 8 bits of information for each color channel. The actual color used is system dependent. The constructor has three main forms, the first taking an integer value in the range of 0 to 255 for each of the red, green, and blue color channels. The second takes an integer value representing a packed 24-bit number. The third form takes floating-point color channel intensities in the range of 0 to 1.0. All three forms of the constructor are shown here:
// create a color from three 8-bit intensities Color aColor = new Color(int red, int green, int blue); // create a color from a packed 24-bit number Color aColor = new Color(int rgb); // create a color from three intensities in the range of 0 - 1.0 Color aColor = new Color(float red, float green, float blue);
Several class variables are also associated with class Color
to define commonly used shades. Table 13.12 lists these variables.
| Color.white
Color.lightGray Color.gray Color.darkGray Color.black Color.red Color.pink Color.orange Color.yellow Color.green Color.magenta Color.cyan Color.blue |
The Dimension class provides an object that has only two properties: width and height. This class is really a helper class used by the rest of the AWT. You may find it a useful addition to your own classes. A Dimension object may be created as shown here:
// create a dimension with a width of 50 and a height of 100 Dimension aDimension = new Dimension(50, 100);
The Point class provides an object that, like Dimension, is a helper class with only two properties. In this case the properties represent spatial x,y coordinates. By defining methods in your classes to accept a Point object any time you want to pass coordinate values, you will find your code easier to read and understand. A Point object may be created using the following:
// create a point at coordinates x=100, y=200 Point aPoint = new Point(100, 200);
The Rectangle class provides an object-oriented representation of a rectangle. Note that the Rectangle class is not the same as the rectangle methods of the Graphics class. You might also notice that class Rectangle is not a Component. It is a child of Object, which reflects the role of Rectangle not as a visual component, but as a helper class for other classes of the AWT.
A Rectangle has only four integer properties: x, y, width, and height. The Rectangle class has a large number of constructor forms, only a few of which are shown here:
// create a rectangle from x, y, width, height Rectangle aRect = new Rectangle(0, 0, 10, 10); // create a rectangle from width and height Rectangle aRect = new Rectangle(10, 10); // create a rectangle getting x and y from a Point Rectangle aRect = new Rectangle(new Point(10,10)); // create a rectangle getting width and height from a Dimension Rectangle aRect = new Rectangle(new Dimension(10,10));
The Rectangle class also has methods for computing the union of two rectangles, computing the intersection of two rectangles, growing a rectangle, and more. The Rectangle class is very handy for keeping track of arbitrary regions of space. One use might be to track dirty rectangles as part of an animation class.
The Polygon class abstracts the concept of a polygon as a list of x and y coordinates. The Polygon class provides methods for adding additional points, testing whether a point is inside the polygon, and calculating the bounding rectangle of the polygon. An example of how to create a Polygon follows:
// create an empty polygon Polygon aPoly = new Polygon(); int xpoints[]; int ypoints[]; int npoints; fillArraysWithVertexData(xpoints, ypoints, npoints); // create a polygon from a list of points Polygon aPoly = new Polygon(xpoints, ypoints, npoints);
The Font class encapsulates information about a font. This class is used as a helper class by the rest of the AWT. (The exact fonts available on any given system vary.) This class has only one constructor; it takes as parameters a font name, a font style, and a font size. In the current AWT implementation, the font style is the arithmetic sum of up to three class variables. These are Font.PLAIN, Font.BOLD, and Font.ITALIC. You may create a Font object as follows:
// create a bold, italic helvectica 24-point font
Font aFont = new Font("Helvetica", Font.BOLD + Font.ITALIC, 24);
The Font class provides some basic methods for determining properties of a given font. These include getName(), which retrieves the name of the font; getSize(), which returns the size of the font; and getStyle(), which returns the arithmetic sum of the font style class variables. Because masking out the bits from the return of getStyle() is tedious, the class also provides three additional methods-isPlain(), isBold(), and isItalic()-that return boolean values. Some examples of the more common methods of Font are shown here:
// get the name of the font String aString = aFont.getName(); // get the font size int aSize = aFont.getSize(); // get the sum of the font style types int aStyle = aFont.getStyle(); // determine font styles boolean aFlag = aFont.isPlain(); boolean aFlag = aFont.isBold(); boolean aFlag = aFont.isItalic();
The Toolkit class provides a method, getFontList(), that returns an array of strings enumerating the fonts available on the system. Call getFontList() as follows:
// get a list of available fonts String[] aFontArray = getFontList();
You are bound to have times when your Java programs need to know some more detailed information about a particular font than the Font class can provide. In these instances, you can use the FontMetrics class. A FontMetric object is created from a Font object using the getFontMetrics() method, as shown here:
// create a bold, italic helvectica 24-point font
Font aFont = new Font("Helvetica", Font.BOLD + Font.ITALIC, 24);
// get information about font
FontMetrics aFontMetric = getFontMetrics(aFont);
The methods provided by FontMetrics calculate some useful values relating to the given font. Some examples of these methods are shown here:
// find the width in pixels of a given string in the font
int aPixelWidth = aFontMetric.stringWidth("Hello");
// find the width in pixels of a given character in the font
int aCharWidth = aFontMetric.charWidth('W');
// get font ascent = distance between baseline and top of char
int aAscent = aFontMetric.getAscent();
// get font descent = distance from baseline to bottom of char
int aDescent = aFontMetric.getDescent();
// leading of font = gap between descent of one line and ascent of next
int aLeading = aFontMetric.getLeading();
// height of font = ascent + descent + leading
int aHeight = aFontMetric.getHeight();
The Canvas class is a child of Component and represents a simple drawing surface. Canvas objects are good for drawing or painting images and other graphics operations. A Canvas object can do very little other than provide a surface on which to draw graphics. It provides a default paint method that simply clears itself to the background color. When you find yourself drawing on a Panel object you should consider adding a Canvas object instead.
The Graphics class is an important member of the AWT. It encompasses most of the methods available for drawing graphics. Rather than create instances of Graphics, however, you receive a reference to an already created Graphics object. For example, the paint() and update() methods of Components (including the Applet class) receive an instance of the Graphic class when they are invoked. Where does the instance come from? Ultimately it comes from the Java system as part of the normal event processing. The following code shows how a Graphics object is passed to your applet:
public void paint(Graphics aGraphic)
{
...
}
The coordinate system used by Graphics should be familiar if you are familiar with programming microcomputers. The x coordinates increase from left to right across the drawing surface. The y coordinates increase from top to bottom down the drawing surface. So the origin is at the top-left corner.
The Graphics class provides support for drawing a complete suite of graphics primitives including lines, rectangles, polygons, ellipses, and arcs. Drawing lines, rectangles, rounded rectangles, and 3D rectangles is easy. The drawLine() method draws a line using the arguments as x,y pairs of the start and end points. The drawRect() method draws a rectangle using the arguments as left, top, right, bottom coordinates. The drawRoundRect() method is similar to drawRect() except the last two arguments specify the horizontal and vertical diameters of the corner arc.
The parameters passed to the draw3DRect() method are similar to drawRect() with the exception of the final parameter, which is a boolean value. If the boolean variable is true, the rectangle is drawn with a raised 3D effect; if it is false, the rectangle is drawn with a lowered 3D effect. Examples of drawLine(), drawRect(), drawRoundRect(), and draw3DRect() are shown here:
public void paint(Graphics aGraphic)
{
// draw a line from (0,0) - (200,200)
aGraphic.drawLine(0, 0, 200, 200);
// draw a rectangle
aGraphic.drawRect(0, 0, 50, 50);
// draw a rounded rectangle
aGraphic.drawRoundRect(0, 0, 50, 50, 5, 5);
// draw a rectangle with raised borders
aGraphic.draw3DRect(0, 0, 50, 50, true);
}
Drawing filled versions of the same basic shapes (except lines, of course) is just as easy. The shapes are filled using the current foreground color, which can be changed with the setColor() method. Examples of drawing filled versions of rectangles, round rectangles, and 3D rectangles are shown here:
public void paint(Graphics aGraphic)
{
// change the foreground color
setColor(Color.magenta);
// draw a filled rectangle
aGraphic.fillRect(0, 0, 50, 50);
// draw a filled round rectangle
aGraphic.fillRoundRect(0, 0, 50, 50, 5, 5);
// draw a filled raised 3D rectangle
aGraphic.fill3DRect(0, 0, 50, 50, true);
}
Drawing polygons is accomplished with the drawPolygon() method, and it is only a little bit more difficult than drawing rectangles. To define a polygon shape, you need to provide a list of vertices; actually, you need to have two separate lists. One list is an array of integers representing the x coordinates. The other list holds the y coordinates. The final argument is the number of vertices in the polygon. Note that the drawPolygon() method does not automatically close a polygon. Therefore, if you want to draw a closed shape, you must provide an extra vertex (just the first vertex again). The following code draws a polygon and a filled polygon:
public void paint(Graphics aGraphic)
{
int xpoints[] = {50, 100, 150, 100, 50};
int ypoints[] = {50, 0, 50, 100, 50};
int npoints = 5;
// draw a diamond shape
aGraphic.drawPolygon(xpoints, ypoints, npoints);
// draw a filled diamond shape
aGraphic.fillPolygon(xpoints, ypoints, npoints);
}
You can also draw a polygon using an instance of the Polygon class discussed previously. This technique tends to be much easier to work with if you need to manipulate many polygons. It eliminates the need to manage significant numbers of integer arrays. All you need to do is create a Polygon object and call the drawPolygon() or fillPolygon() method. Note the absence of a draw3DPolygon() method. Besides being of questionable utility, it would be a very difficult routine to implement correctly. The following code draws a polygon and a filled polygon using the Polygon class:
public paint(Graphics aGraphic)
{
// get a Polygon object
Polygon aPoly = someMethodWhichCreatesPolygons();
// draw the polygon
aGraphic.drawPolygon(aPoly);
// draw a filled polygon
aGraphic.fillPolygon(aPoly);
}
In discussing some of the capabilities of the Graphics class above, I mentioned that methods were provided for drawing nearly every imaginable shape. One shape that was missing was the circle. Was that an accidental omission from the Java language? No. In effect, a circle is just a special case of a more general shape, the ellipse. Now some might argue, and I might agree, that a circle is such a common special case of the ellipse that it deserves its own method.
In any case, the Graphics class does provide drawOval() and fillOval() methods that work just fine for drawing and filling ellipses and circles. Using these methods could hardly be easier. Both methods take four integer arguments. The first two are the x and y coordinates of the top-left corner; the last two arguments are the width and height of the oval. These arguments essentially specify the bounding box of the ellipse. Examples of the drawOval() and fillOval() methods are shown here:
public paint(Graphics, aGraphic)
{
// draw a horizontally elongated ellipse
aGraphic.drawOval(100, 100, 150, 100);
// now draw a circle, radius is 100/2 = 50
aGraphic.drawOval(100, 100, 100, 100);
// now fill the circle
aGraphic.fillOval(100, 100, 100, 100);
}
You might be thinking that this syntax is a bit cumbersome because you don't normally think of circles and ellipses in terms of their bounding boxes. If you are like me, you probably think in terms of the center point and horizontal and vertical diameters (or perhaps radii).
Why doesn't the AWT provide a method with these arguments? The reason is that it can't! At least not easily. The explanation goes back to the (reasonable) restriction of Java that polymorphic methods must differ, at a minimum, either in the number or the type of their arguments. Because the proposed method would also take four integer arguments, it would violate that rule.
Are we defeated? No, not really, because the Java AWT could provide a drawOval() method in the Graphics class that does take different arguments. Unfortunately, the current AWT does not provide these methods. The following code shows one possible approach. Both the Point and Dimension classes pass the same basic information to the drawOval() method, but each class uses arguments of different types.
// possible new methods for Graphics public void drawOval(Point aPoint, Dimension aDimension); public void drawOval(int X, int Y, Dimension aDimension);
You could simply subclass one of the Graphics-derived classes and provide these new methods. Actually, the workaround is a bit more complicated because you would also need to hook into the AWT to make sure that the paint() and update() methods were passed instances of the new Graphics class. I will leave that problem, as they say, as an exercise for the reader. Perhaps Sun will provide these methods in a later release of the Java Developer's Kit (JDK).
Drawing and filling arcs is similar to drawing ellipses; after all, an arc is really just a portion of an ellipse. As it turns out, the implementation of drawArc() is a bit harder to work with in practice. The first four arguments are the same as for drawOval()-specifying the starting x and y coordinates, width, and height. The last two are a bit tricky. The fifth argument defines the starting angle for the arc, and the sixth is the angle swept out by the arc.
For the starting angle, the AWT defines angles as increasing in a counterclockwise direction with 0 degrees at the 3:00 position. Therefore, 90 degrees is at 12:00, 180 degrees is at 9:00, and 270 degrees is at 6:00. Remember that the sixth argument specifies the angle swept out by the arc relative to the starting angle. In the following example, the arc starts out at 0 degrees (that is, 3:00). The arc sweeps through 270 degrees (counterclockwise) to draw three quarters of a circle stopping at 6:00. If the sixth argument had been negative, the arc would have been drawn in a clockwise direction. The drawArc() and fillArc() methods are used in the following code:
public paint(Graphics aGraphics)
{
// draw a 3/4 circle starting at 0 degrees sweeping out 270 degrees
aGraphics.drawArc(100, 100, 100, 100, 0, 270);
// fill the same arc
aGraphics.fillArc(100, 100, 100, 100, 0, 270);
}
The fillArc() method connects the two arc end points to the center of the circle or ellipse forming a closed shape. This method is useful for drawing pie wedges and other shapes.
Drawing text is also easy with the Graphics class. The method drawString() draws text using the currently selected font. The first argument is the text string to draw, and the remaining two arguments define the x and y coordinates at which to draw the text. The coordinates specify the bottom-left corner of the string, as shown here:
public paint(Graphics aGraphic)
{
// draw text at coordinates (10, 10)
aGraphic.drawString("Hello there!", 10, 10);
}
The Image class is an encapsulation of a graphic image. Its purpose is to serve as a container for graphic images. The Image class can also provide information about a graphic image. An instance of the Image class is usually retrieved using the getImage() method of the Applet class. The Image class can hold any type of image that is supported by Java and most browsers. Right now that list is limited to GIF and JPG files.
A typical use of an Image object involves retrieving a graphic image using getImage() and then displaying the image using the drawImage() method of the Graphics class. The most common form of the getImage() method takes two parameters. The first is a URL object that points to the base location of the image. The second is a String object representing the path to the image file relative to the base location. Commonly, the Applet class methods getDocumentBase() and getCodeBase()provide the URL object. The following example shows how the getImage() and drawImage() methods may be used:
public paint(Graphics aGraphic)
{
// retrieve an image object
Image aImage = getImage(getDocumentBase(), "images/redball.gif")
// display the image
aGraphic.drawImage(aImage, 20, 20, this)
}
The Label class represents the simplest of all the components in the AWT. It represents a static text label. Label components don't do very much and are most often used simply to label other components on a panel. A Label object is constructed using one of the following constructor forms:
// an empty label with no text
Label aLabel = new Label();
// using a String object
Label aLabel = new Label("I am a label!");
// using a String object and an alignment specification
Label aLabel = new Label("I am also a label!", Label.CENTER);
The third form of the constructor shown here allows the text alignment of the label to be specified. The class variables Label.LEFT, Label.RIGHT, and Label.CENTER give the allowable values.
Because a label is a component, it provides interfaces that allow the AWT to manage its layout. A label component also knows how to respond to AWT system events such as a request to repaint itself. These types of default behaviors distinguish a component from a simple text string drawn to the screen.
The font of a label is controlled by the setting in its parent class Component. By default, every component uses the same font as its parent. The font may be changed, however, by making this call to the setFont() method:
// change the label font
aLabel.setFont(new Font("Helvetica", Font.BOLD, 12));
Remember that every Applet is a Panel so that we may now display our newly created label object using the following code. The add() method of class Applet is inherited from class Container. Events that are received by the Applet are now sent along to the label and any other objects contained by the applet. A Label object may be added to a Container by calling the add() method:
// add our label to the applet container
add(aLabel);
// add a label without saving an explicit reference
add(new Label("Another label", Label.RIGHT));
Other methods provided by the Label class include getText(), setText(), getAlignment(), setAlignment(), and any of the methods inherited from Component.
Button objects are only a little more complicated than Label objects. In addition to having its own text label, a button must be able to respond to mouse clicks. The way that a button responds to user events is included in the discussion of events a bit later in the chapter.
The constructor forms for the Button class are very simple. The first form creates a plain button with no labeling text. Why would anyone want such a button? One such case is when the text of the button is undefined, but you still want to reserve layout space for a fixed number of button objects. A Button object is created using one of the following constructor forms:
// create a blank button
Button aButton = new Button();
// create a button with a label
Button aButton = new Button("OK");
Like Label objects, Button objects can be added to a Container and displayed via a call to the add() method.
A check box is an object that has a label and a single binary state of either on/off, or selected/unselected if you prefer to think of it that way. Changing the state of a check box does not typically result in any action other than updating the state of a program.
Check boxes are also frequently combined into groups of check boxes for the purposes of allowing selection from a set of alternative choices. When used in this manner, the check boxes are often called "radio buttons," referring to the way buttons on a car radio are pushed to select one of several radio stations.
The following code shows the constructors for the Checkbox class. The first form creates an unlabeled and unselected check box. The second form creates a check box using a string object to provide the label test. The third form is a bit more complicated. The first parameter is a string object that specifies the label text, and the second parameter specifies radio button groups (more on that topic later). The third parameter is a boolean argument that specifies whether the check box is initially on or off. All three forms of the Checkbox constructor are shown here:
// create an empty, unselected check box
Checkbox aCheckbox = new Checkbox();
// create a checkbox using a string object for a label
String aString = "Show Results";
Checkbox aCheckbox = new Checkbox(aString);
// create a selected checkbox
Checkbox aCheckbox = new Checkbox("Show Results", null, true);
// create some checkboxes and add them to the applet container
add(new Checkbox("Show Results"));
add(new Checkbox("Show More Results", null, true));
Some additional methods provided by the Checkbox class are getLabel(), setLabel(), getState(), and setState(). These methods provide a means for getting and setting the label text and check box state. The getLabel() method returns a string object, and setLabel() takes a string object as an argument. The setState() and getState() methods take and return boolean values, respectively. The following code shows how to use each of these methods:
// get the checkbox state
boolean flag = aCheckbox.getState();
// set the checkbox state
aCheckbox.setState(true);
// get the checkbox label text
String aString = aCheckbox.getLabel();
// set the checkbox label text
aCheckbox.getLabel("Self Destruct");
As discussed earlier, when several check boxes are grouped together into a mutually exclusive set they are called radio buttons. If you look at the Java API documentation, however, you won't find a class called RadioButton. How do you create radio buttons then? The answer lies in a special class called CheckboxGroup that manages a set of check boxes so that they behave like radio buttons. Applying CheckboxGroup really is almost as easy as it sounds. The following code is a simple example of grouped check boxes:
// create a checkbox group manager
CheckboxGroup aCheckboxGroup = new CheckboxGroup();
// create and display our radio buttons
add(new Checkbox("1st floor",aCheckboxGroup, true));
add(new Checkbox("2nd floor",aCheckboxGroup, false));
add(new Checkbox("3rd floor",aCheckboxGroup, false));
add(new Checkbox("4th floor",aCheckboxGroup, false));
Remember that only one radio button can be selected at a time. How does it work? The process is controlled by the CheckboxGroup class. What would happen if you tried to set the state of each checkbox to true in the code above? The Checkbox class contains code that sets itself to be the only active radio button if an instance of CheckboxGroup is passed, rather than null, in the constructor. So in this case, only the last button added would be selected.
The Checkbox class provides two methods to change the group to which it belongs: getCheckboxGroup() and setCheckboxGroup(). The CheckboxGroup class provides the methods getCurrent() and setCurrent() to get or set the selected radio button. These methods are easy to use, as shown by the following code:
// assign the checkbox to a newly created group aCheckbox.setCheckboxGroup(new CheckboxGroup()); // retrieve the group to which the checkbox belongs CheckboxGroup aCheckboxGroup = aCheckbox.getCheckboxGroup(); // retrieve the currently selected checkbox Checkbox aCheckbox = aCheckboxGroup.getCurrent(); // manually set the selected checkbox aCheckboxGroup.setCurrent(aCheckbox);
The Java AWT provides for a visual component called the choice menu. I find this name a bit misleading because this component is not a menu as much as a pull-down list of radio buttons. Only one item in the list may be selected at a time, and it is normally the only item visible. When the user clicks on the object, it displays a pull-down list of all the available choices. In the Windows environment this element is called a combo box.
The component class Choice is used to create a choice menu, and the addItem() method populates it. The constructor is very simple, taking no parameters at all, as shown in the following example:
// create an empty choice menu
Choice aChoice = new Choice();
// add some items to the menu list
aChoice.addItem("First class");
aChoice.addItem("Business class");
aChoice.addItem("Economy");
aChoice.addItem("Coach");
aChoice.addItem("Baggage Compartment");
The only form of addItem() provided by class Choice takes a string object as an argument. Therefore, all menu choices must be string objects. However, the class maintains the list of menu choices in a Vector object that is part of the Choice class, so it is theoretically possible to store any type of object as a choice.
Because of this design, if you wanted to have a set of numeric choices (perhaps airplane models), you could use one of the object wrapper classes to convert the number to a string object. The following code shows several methods for adding numeric data to a list of choices. If you know the set of choices at design time, you can explicitly write the numbers as string objects. However, if you need to add arbitrary numbers to a list, you may have to use an object wrapper to recast the number as a string. The following example shows how to add numeric items to a choice menu:
// illegal! Integer primitives are not objects
aChoice.addItem(747);
// ok, all arguments are objects
aChoice.addItem("727");
aChoice.addItem(Integer(727).toString());
aChoice.addItem(Integer(737).toString());
aChoice.addItem(Integer(747).toString());
aChoice.addItem(Integer(767).toString());
aChoice.addItem(Integer(aNumber).toString());
The Choice class provides several additional methods (shown in the next code sample). The getItem() method returns a string object representing the item at a given index. The countItems() method returns the total number of choices in the menu. The getSelectedIndex() and getSelectedItem() methods return the index and string of the currently selected choice. The two forms of the select() method change the current selection based on either the choice index or the string. Each of these methods is demonstrated here:
// return the string representing the given choice
String aString = aChoice.getItem(1);
// get the number of items in the menu
int nItems = aChoice.countItems();
// get the index of the selected item
int nSelected = aChoice.getSelectedIndex();
// get the string representing the selected choice
String aString = aChoice.getSelectedItem();
// select the item at index 1
aChoice.select(1);
// select the choice labeled with the given string
aChoice.select("First Class");
A text field is a component that provides an editable text field. Windows users might recognize this field as a single-line edit box. Text fields do not have scrollbars and can handle only a single line of text. Also note that the text field is not labeled; it includes only a small frame and the editable text field. Consequently, a Label component is often used in conjunction with a text field to identify its purpose. There are several ways to construct a text field:
// create an empty text field
TextField aTextField = new TextField();
// create a text field 10 chars wide
TextField aTextField = new TextField(10);
// create a text field with some default text
TextField aTextField = new TextField("Some text");
// create a text field with some default text, 10 chars wide
TextField aTextField = new TextField("Some text",10);
// add a text field to the display
add(aTextField);
You can also create a text field that hides the text entered into it. This technique is used mainly for password fields. The setEchoCharacter() method tells the TextField object to echo a given character rather than the entered text. You must first create the text field:
// create a text field with some default text
TextField aTextField = new TextField("Some text");
// set the echo character
aTextField.setEchoCharacter('*');
// display the text field
add(aTextField);
TextField provides many additional methods that provide a number of useful capabilities. Some examples of these methods follow:
// retrieve the text
String aString = aTextField.getText();
// set the text
aTextField.setText("Some text");
// get width of the text field
in nColumns = aTextField.getColumns();
// select text between start and end columns
aTextField.select(start, end);
// select the entire text field
aTextField.selectAll();
// true or false if field is editable
boolean aFlag = aTextField.isEditable();
// toggle editable state on or off
aTextField.setEditable(true);
// get the echo character
char aChar = aTextField.getEchoChar();
// true of false if echo character is set
boolean aFlag = aTextField.echoCharIsSet();
The List class represents a scrollable list box component. The list may have a specified number of visible rows. If the list contains more rows than visible rows, a scrollbar is created to allow scrolling through the entire list. A List object may also permit the user to make multiple selections. Both of the List constructor methods are used here:
// construct an empty list with no visible lines List aList = new List(); // construct a list with 10 rows and multiple selections List aList = new List(10, true);
Additional methods are provided to add, replace, and delete items from the list box; they are addItem(), replaceItem(), and delItem().The following code demonstrates how to add items to a list object. After creating a List object and adding items to it, you would normally add() the List component itself to a Panel object, perhaps as in the following code:
// add some items to our list
aList.addItem("Pastrami");
aList.addItem("Capicola");
aList.addItem("Corned beef");
// add the List to a Panel object
aPanel.add(aList);
The TextArea class represents a multiline editable text region. Unlike TextField objects, a TextArea can handle significant amounts of text. If needed, the TextArea object displays scrollbars to allow the user to browse through the text. Here are the constructors for the TextArea: class
// create an empty text area
TextArea aTextArea = newTextArea();
// create an empty text area with a size of 10 rows by 40 columns
TextArea aTextArea = newTextArea(10, 40);
// create a text area with some text
TextArea aTextArea = newTextArea("A little bit of text");
// create a text area with some text and a size of 10 rows by 40 columns
TextArea aTextArea = newTextArea("A little bit of text", 10, 40);
Some additional methods provided by TextArea are getColumns(), getRows(), insertText(), appendText(), and replaceText(). Example usage of these methods follows:
// get the number of rows
int aColumns = aTextArea.getColumns();
// get the number of columns
int aRows = aTextArea.getRows();
// insert text starting at position 0
aTextArea.insertText("A little bit more text",0);
// append text to end
aTextArea.appendText("This is the end!");
// replace 'A little' with 'A '
aTextArea.replaceText("A ",0,8);
The Scrollbar class is a visual component that is used as a helper class by other classes in the AWT and is not often used by itself. However, sometimes you may want to work directly with an instance of this class. For example, you may want to create a standalone scrollbar, or "slider" as it is sometimes called, to allow users to visually choose from a range of possible values.
The scrollbar range is determined by minimum and maximum properties. Likewise, the scrollbar horizontal or vertical orientation is determined by an orientation property.
Examples of the constructors for class Scrollbar follow. The first form creates a vertical scrollbar with minimum and maximum values of 0. The second form creates a scrollbar with an orientation specified by a class variable-either Scrollbar.HORIZONTAL or Scrollbar.VERTICAL.
The third form is more complicated because it provides the most flexibility. It has a total of five integer arguments. The first argument is the scrollbar orientation, and the second is the initial value of the scrollbar that must be within the minimum and maximum range values. The third argument specifies the total height or width of the scrollbar depending on the scrollbar orientation. The last two arguments are the minimum and maximum range values. Both methods of constructing a Scrollbar are shown here:
// create a vertical scrollbar with 0,0 min and max values Scrollbar aScrollbar = new Scrollbar(); // create a vertical scrollbar with 0,0 min and max values Scrollbar aScrollbar = new Scrollbar(Scrollbar.VERTICAL); // create a horizontal scrollbar, initial value 0, width of 30 // and a range of 0 to 100 Scrollbar aScrollbar = new Scrollbar(Scrollbar.HORIZONTAL, 0, 30, 0, 100);
The Window class is a descendant of Container (and therefore of Component) that provides the ability to create windows. Because windows are container objects, you can add components to them just like you would to a Panel object.
These windows exist outside the frame of the browser application. Descendants of class Windows may have title bars, menus, sizing frames, and so on. Although not technically an abstract class, it is rare to instantiate an object of this class. More commonly, Java applets and applications will use one of the subclasses that provide additional capabilities.
The Frame class is a subclass of the Windows class and provides a more complete implementation of a GUI window. It implements the MenuContainer interface that allows it to have pull-down menu bars. Frames are initially invisible and, unlike Panel objects, their layout manager defaults to BorderLayout.
Methods are available for resizing and moving windows. Because a frame window is initially invisible, you must use the show() method to display it. The hide() method can then be used to hide the window. The following sample code shows how to create a Frame, change the size using resize(), move the window, and then display it using show():
// create a frame window
Frame aFrame = new Frame("I am a Frame Object");
// change window size
aFrame.resize(200, 200);
// move window to 100, 100
aFrame.move(100, 100);
// make window visible
aFrame.show();
The Dialog class is another child of class Windows. The Dialog class provides behavior common to dialog boxes. Dialog objects are similar to Frame objects in many ways. The main differences are that dialog boxes tend to be used for displaying messages and for getting inputs from the user. Therefore, dialog boxes do not usually have menus or title bars, although they can have them.
Dialog objects may be either modal or nonmodal. A modal Dialog blocks or waits until it is completed before allowing input to any other windows. A nonmodal Dialog may be hidden behind another Window object and does not block user input. A Dialog must always have a parent Frame so a dialog box cannot exist all by itself. Note however, the parent does not have to be visible. Like the Frame class, a Dialog is invisible by default, so you must use the show() method to display it. The second parameter to the Dialog constructor is a boolean value that determines whether the dialog is modal or nonmodal. If the boolean value is true, the dialog is modal; if the value is false, then it is nonmodal. The following code shows how to create both modal and nonmodal Dialog objects:
// create a modal dialog Dialog aDialog = new Dialog(aFrame, true); // create a nonmodal dialog with a title Dialog aDialog = new Dialog(aFrame, "Warning", false);
The FileDialog class is a special-case subclass of the Dialog class. It provides a dialog box customized for file open and file save dialog boxes. The AWT maps down to the standard operating system dialogs if any exist. The two constructors provided by FileDialog allow you to create file dialogs with titles. The first form is used for file open dialogs and specifies the parent Frame along with a title string:
// create a file open dialog box w/ title FileDialog aFileDialog = new FileDialog(aFrame, "Open File");
The second form also specifies a parent Frame and a title string. The third parameter specifies whether the dialog should be a file open or a file save dialog box. The FileDialog class provides two class variables-FileDialog.OPEN and FileDialog.SAVE-to make things easier. By using class variables to define the behavior of the FileDialog class, it is possible for future releases of the JDK to support additional standard dialog boxes such as a file save as dialog. The second form is usually used only for file save dialog boxes, but it can create file open dialogs as well:
// create a file save dialog using class variable FileDialog aFileDialog = new FileDialog(aFrame, "Save File", FileDialog.SAVE);
As discussed earlier, the AWT provides a flexible and platform-independent means of specifying screen layouts. Rather than hard-coding screen x,y coord