This chapter will answer the following questions:
Most programming languages rely on pre-built libraries to support certain functionality. For example, C has no built-in support for I/O functions. JDK 1.1 comes with a very impressive library that includes support for database connectivity, GUI design, I/O, and network programming. Although JDK 1.1 contains numerous support packages, there are ten standard packages that warrant further discussion. The following table briefly describes these packages.
Package |
Description |
Language |
The main core of the Java language |
Utilities |
Support for utility data structures |
I/O |
Supports various types of input/output |
Networking |
TCP/IP support and socket programming |
AWT |
GUI design and event-handling |
Text |
Support for internationalization |
Security |
Support for cryptographic security |
RMI |
Support for distributed programming |
Reflection |
Used to obtain run-time class information |
SQL |
Support for querying databases using SQL |
This chapter will primarily focus on the Language, Utilities and I/O packages. Aspects of the security package will be discussed in the "Java Virtual Machine Security" chapter.
One of the most important packages in the Java class library is the java.lang package. This Language package contains the language's main support classes. It is virtually impossible to write a Java program without using the Language package. The following sections discuss some of the more important classes contained in the Language package.
The Object class is the parent class of all Java classes. This simply means that all Java classes are derived from the Object class. The object class itself contains several methods of importance. These methods include clone, equals, and toString.
An object that uses the clone method simply makes a copy of itself. To accomplish this, new memory is allocated for the clone, then contents of the original object is copied into the clone object. For example, a copy of the Document class that contains a text and author property needs to be created. To create a new instance of the Document class that contains both properties and the values associated with the object, the clone method should be used. The following code demonstrates how this would be accomplished.
Document document1 = new Document ("docText.txt", "Joe Smith"); Document document2 = document1.clone();
The equals method compares two objects of the same type for equality by comparing the properties of both objects. It simply returns a Boolean value depending on the results of the object that calls it and the object that is passed to it. For instance, if equals is called by an object that passes it an object that is completely identical, the equals method would return a true value.
The toString method returns a String representing the value of the object. For this method to return proper information about different types of objects, the object's class must override it.
Primitive data types are not used as objects in Java. These primitive data types include numbers, booleans, and characters. The reasoning behind not including these data types as objects, lies in performance.
Treating these primitive data types as objects would greatly impact the language's performance (due to the overhead of processing objects). However, some Java classes and methods require primitive data types to be objects. Also, it would be useful in some cases to add custom methods to these types. For these reasons, the Java type wrapper classes can be instantiated. The following lists the primitive data types that the Language packages support as objects.
Type Wrapper Classes |
Description |
Boolean |
True or False (1 Bit) |
Byte |
-128 to 127 (8 bit signed integer) |
Character |
Unicode character (16 bit) |
Double |
+1.79769313486231579E+308 to +4.9406545841246544E-324 (64 bit) |
Float |
+3.40282347E+28 to +1.40239846E-45 (32 bit) |
Integer |
-2147483648 to 2147483647 (32 bit signed integer) |
long |
-9223372036854775808 to 9223372036854775807 (64 bit signed integer) |
short |
-32768 to 32767 (16 bit signed integer) |
Although each one of these classes contains its own methods, several methods are standard throughout each object. These methods include ClassType, typeValue, toString and Equals.
The ClassType method is the constructor for the wrapper classes. It simply takes an argument of the class type it is wrapping. For example, the following code demonstrates how a Character wrapper class is constructed.
Character charWrapper = new Character ('T');
The typeValue method returns the primitive type of the wrapper class. The following code demonstrates using the charWrapper object. Notice that charPrimitive is a primitive data type (declared as a char).
However, using this method, primitive data types can be assigned to type wrappers.
char charPrimitive = charWrapper.charValue();
The toString and Equals methods are used similarly as in the Object class. They are typically used for debugging purposes.
The Math class provides useful methods that implement common math functions. This class is not instantiated, and it is declared final, so it cannot be subclassed. Some of the methods included in this class are: sin, cos, exp, log, max, min, random, sqrt and tan. Some of these methods are overloaded to accept and return different data types. Here are examples of using some of the methods.
double d1 = Math.sin (45); double d2 = 23.4; double d3 = Math.exp (d2); double d4 = Math.log (d3); double d5 = Math.max (d2, Math.pow (d1, 10);
The Math class also declares the constants PI and E. This can easily be used within any calculations.
Do not confuse the Math class with the java.math package. The java.math package provides support classes for working with large numbers.
The String class is used to declare and manipulate strings. Unlike C/C++, Java does not use character arrays to represent strings. The String class is used for constant strings and is typically constructed when the Java compiler encounters a string in quotes. However, strings can be constructed several ways. The following lists some of the strings constructors and what they accept.
Constructor |
Parameters |
String |
() |
String |
(String) |
String |
(char value[]) |
String |
(char value[], int off, int count) |
String |
(StringBuffer) |
The String class contains several important methods that are essential when dealing with strings.
The following table lists some of the more crucial methods and declares what they accept and return.
Method |
Accepts |
Returns |
length |
() |
int |
charAt |
(int index) |
char |
compareTo |
(String) |
int |
startsWith |
(String prefix) |
boolean |
endsWith |
(String suffix) |
boolean |
indexOf |
(int ch) |
int |
substring |
(int beginIndex, int endIndex) |
String |
concat |
(String) |
String |
toLowerCase |
() |
String |
toUpperCase |
() |
String |
valueOf |
(Object) |
String |
A very efficient and feature associated with many of these methods is that they are overloaded for different data types. The following demonstrates how the string class and some of its methods can be used.
String s1 = new String ("Hello World."); char cArray[] = {'J', 'B', 'u', 'i', 'l', 'd', 'e', 'r'}; String s2 = new String (cArray); //s2 = "JBuilder" int i = s1.length(); //i = 12 char c = s1.charAt(4); //c = 'o' i = s1.indexOf('l'); //i = 2 (the first 'l') String s3 = "abcdef".substring (2, 5) //s3 = "cde" String s4 = s3.concat ("f");//s4 = "cdef" String s5 = valueOf (i); //s5 = "2" (valueOf()is static)
The StringBuffer class differs from the String class in that StringBuffer objects can be modified. Like the String class, the StringBuffer class has several constructors. The following table lists these constructors for the StringBuffer class.
Constructor Accepts |
|
StringBuffer |
() |
StringBuffer |
(int length) |
StringBuffer |
(String) |
There are several important methods that separate the StringBuffer class from the String class. These methods include: Length, Capacity, setLength, charAt, setCharAt, Append, Insert and toString.
One method that is used quite often when dealing with StringBuffers' is the Append method. The Append method is used to add text to the StringBuffer. Fortunately, the Append method is heavily overloaded.
Another important method that is commonly used with the StringBuffer is the Capacity method. This method returns the amount of memory allocated for the StringBuffer. This value can be greater than the value returned by the Length method. This is due to the fact that memory allocated for a StringBuffer can be controlled with the StringBuffer (int length) constructor. The following code demonstrates some of the methods associated with the StringBuffer class.
StringBuffer s1 = new StringBuffer(10); int c = s1.capacity(); //c = 10 int l = s1.length(); //l = 0 s1.append("Bor"); //s1 = "Bor" s1.append("land"); //s1 = "land" c = s1.capacity(); //c = 10 l = s1.length(); //l = 7 s1.setLength(2); //s1 = "Bo" StringBuffer s2 = new StringBuffer("Helo World"); s2.insert (3, "l"); //s2 = "Hello World"
The System class provides access to system platform independent resources. It is a declared as a final class, so it can't be sub-classed. It also declares its methods and variables as static. This simply allows it to be available, without it being instantiated.
The methods in the system class provide many uses. One important feature is the ability to get the current system time, using the currentTimeMillis method. It is also possible to retrieve and change system resources using the Get and Set Properties methods. Another convenient feature that the System class provides is being able to force garbage collection with the gc method; and finally, the System class allows developers to load dynamic link libraries with the loadLibrary method.
The most useful aspect of the System class is the variables it declares. These variables are used to interact with the system. These variables include in, out, err. The in variable represents the system's standard input stream, whereas the out variable represents the standard output stream. The err variable is the standard error stream. Streams will be discussed in more detail in the I/O Package section.
The java.util package contains various utility classes and interfaces that are crucial for Java development. For the most part, they aid the developer in designing different types of data structures. The following table describes some of the classes associated with the Utilities Package.
Class |
Description |
Calendar |
Allows use of calendar features |
Date |
Represents dates and times |
Dictionary |
An abstract class that is the parent class of Hashtable |
Hashtable |
Allows key associations with values |
SimpleTimeZone |
Represents a time zone |
Stack |
Allows arrangement of objects in a stack |
StringTokenizer |
Breaks String up into tokens |
Vector |
Implements a dynamic array of objects |
The Utility Package also declares three interfaces. These interfaces are Enumeration, EventListener and Observable. In this section, we will only cover the Vector class and the Enumeration interface.
The Enumeration interface is used to implement a class capable of enumerating values. A class that implements the Enumeration interface can facilitate the traversal of data structures.
The methods defined in the Enumeration interface allow the Enumeration object to continuously retrieve all the elements from a data structure, one by one. There are only two methods declared in the Enumeration interface, hasMoreElements and nextElement.
The hasModeElements method returns true if more elements remain in the data structure. The nextElement method is used to return the next object in the structure being enumerated.
A simple example will be used to illustrate the implementation of the Enumeration interface. This example will contain a class called canEnumerate, which implements the Enumeration interface. An instance of that class can be used to print all the elements of a Vector object (in this case v).
canEnumerate enum = v.elements(); while (enum.hasMoreElements()) { System.out.print (enum.nextElement()); }
There is one limitation on an Enumeration object; it can only be used once. There is no method defined in the interface that allows the Enumeration object to backtrack to previous elements. So, once it enumerates the entire list, it is consumed.
JDK 1.1 does not include support for many dynamic data structures, such as linked lists and queues; it only defines a Stack class. However, the Vector class provides an easy way to implement dynamic data structures. The following table lists some of the more important methods of the Vector class and declares what they accept.
Method |
Accepts |
Vector |
(int initialCapacity) |
Vector |
(int initialCapacity, int capacityIncrement) |
setSize |
(int newSize) |
capacity |
() |
size |
() |
elements |
() |
elementAt |
(int) |
firstElement |
() |
lastElement |
() |
removeElementAt |
(int index) |
addElement |
(Object) |
toString |
() |
The Vector class is efficient because it allocates more memory than needed when adding new elements. A Vector's capacity, therefore, is usually greater than its actual size. The capacityIncrement parameter in the second constructor indicates a Vector's capacity increase whenever an element is added to it.
The following code demonstrates the use of the Vector class. In the code, a Vector object is created called vector1, and enumerates its elements in three ways: using Enumeration's nextElement method, using Vector's elementAt method, and using Vector's toString method. To display the output, an AWT component is created called textArea, and the text property is set using the setText method. Here is the code added to the end of the VectorTest1 constructor.
Vector vector1 = new Vector (10, 2); //initial size is 10, //capacityIncrement is 2 for (int i=0; i<10;i++) vector1.addElement(new Integer(i)); //addElement doesn't //accept //int types //enumerate vector1 Enumeration e = vector1.elements(); frame.textArea1.setText ("The elements using Enumeration's nextElement():\n"); while (e.hasMoreElements()) frame.textArea1.setText (frame.textArea1.getText()+ e.nextElement()+ " | "); frame.textArea1.setText (frame.textArea1.getText()+ "\n\n"); //enumerate using the elementAt() method frame.textArea1.setText (frame.textArea1.getText()+ "The elements using Vector's elementAt():\n"); for (int i=0; i< vector1.size();i++) frame.textArea1.setText (frame.textArea1.getText()+ vector1.elementAt(i) + " | "); frame.textArea1.setText (frame.textArea1.getText()+ "\n\n"); //enumerate using the toString() method frame.textArea1.setText (frame.textArea1.getText()+ "Here's the vector as a String:\n"); frame.textArea1.setText (frame.textArea1.getText()+ vector1.toString());
The following figure demonstrates what this code would accomplish if it was used within an application.
Figure A.1 - Vector and Enumeration example
The java.io package provides support for reading and writing data to and from different devices. The classes in this package can be divided into three different packages. These packages are Input stream classes, Output stream classes, File classes and the StreamTokenizer class.
An Input stream is used to read data from an input source (e.g. a file, a string, memory, etc.). There are several classes encapsulated in this definition. These input stream classes include: InputStream, BufferedInputStream, DataInputStream, FileInputStream and StringBufferInputStream.
The basic method of reading data using an Input stream class is always the same: (1) create an instance of an input stream class, then (2) tell it where to read the data. Input stream classes read data as a continuous stream of bytes. If no data is currently available, the Input stream class blocks (waits until some data becomes available).
In addition to the Input stream classes, the I/O package provides reader classes that correspond to all the Input stream classes (except for DataInputStream). These reader classes are as followed: Reader, BufferedReader, FileReader and StringReader. Reader classes are identical to Input stream classes, except that they read Unicode characters instead of bytes.
The InputStream class is an abstract class from which all other Input stream classes are derived. It provides the basic interface for reading a stream of bytes. The following table lists some of the InputStream methods and what they except. Each of these methods returns an int value, except for the close method.
Method |
Accepts |
read |
() |
read |
(byte b[]) |
read |
(byte b[], int off, int len) |
available |
() |
skip |
(long) |
close |
() |
The first method, abstract int read, reads a byte from the input stream and returns it as an integer (you can cast the return type to char). It returns -1 when it reaches the end of the stream. The second method, int read(byte b[]), reads multiple bytes and stores them in its array parameter. It returns the number of bytes read, or -1 when the end of the stream is reached. The last read method, int read(byte b[], int off, int len), allows the developer to set the maximum number of bytes to read and directs where in the array to store them.
The int available method, returns the number of input bytes that can be read without blocking. The skip method skips a specified number of bytes from the stream. Finally, the close method is used to close the input stream. This method is normally called automatically, but it is safer to call it, manually.
The FileInputStream class is very similar to the InputStream class, only it is designed for reading from files. It contains two constructors. These constructors are FileInputStream(String name) and FileInputStream(File file). The first constructor takes the file's name as a parameter. The second simply takes a file object. The file class will be discussed later. The following example demonstrates the use of the FileInputStream class.
import java.io.*; class fileReader { public static void main (string args[]) { byte buff[] = new byte [80]; try { InputStream fileIn = new FileInputStream("Readme.txt"); int i = fileIn.read(buff); } catch(FileNotFoundException e) { } catch(IOException e) { } String s = new String(buff); System.out.println(s); } }
In this example, a character array was created that will store the input data. Then a FileInputStream object was instantiated and passed the input file's name to its constructor. Next, the FileInputStreams' read() method was used to read a stream of characters and store them in the buff array. The first 80 bytes are read from the Readme.txt file and stored in the buff array. The FilerReader class could be used in place of the FileInputStream method. The only changes needed would be a char array used in place of the byte array, and the reader object would be instantiated as follows.
Reader fileIn = new FileReader("Readme.txt");
Finally, in order to see the result of the read call, a string object is created using the buff array, and then it is passed to the System.out.println method.
As mentioned earlier, the class System is defined in java.lang and provides access to system resources. System.out is a static member of System and represents the standard output device. The println method was called to send the output to the standard output device. The System.out object is of type PrintStream, which will be discussed in the Output Stream Classes section.
Another static member of the System class is the System.in object, which is of type InputStream. Naturally, this object represents the standard input device.
The Output stream classes are the counterparts to the Input stream classes. They are used to output streams of data to the various output sources. The main output stream classes in Java are: OutputStream, PrintStream, BufferedOutputStream, DataOutputStream and FileOutputStream.
To output a stream of data, an output stream object is created and is directed to output the data to a particular output source. As expected, there are also writer classes. There is a corresponding writer class for all, except the DataOutputStream class. Since the OutputStream class is the counterpart to the InputStream class, it defines the following methods.
Method |
Accepts |
write |
(int) |
write |
(byte b[]) |
write |
(byte b[], int off, int len) |
flush |
() |
close |
() |
The flush method is used to flush the output stream (i.e. it forces the output of any buffered data). The PrintStream class is primarily designed to output data as text. It has two constructors. These constructors are PrintStream(OutputStream) and PrintStream(OutputStram, boolean autoflush).
There is one difference between the two constructors. The first causes the PrintStream object to flush the buffered data based on specified conditions, while the second flushes the data when it encounters a new line character (if autoflush is set to true).
Here are some of the methods PrintStream defines.
Method |
Accepts |
checkError |
() |
|
(Object obj) |
|
(String s) |
println |
() |
println |
(Object obj) |
The print and println methods are overloaded for different data types. The checkerror method flushes the stream and returns a false value if an error was encountered.
The FileInputStream and FileOutputStream classes only provide basic functions for handling file input and output. The java.io package provides the File class and RandomAccessFile class for more advanced file support. The File class provides easy access to file attributes and functions. The RandomAccessFile class provides various methods for reading and writing to and from a file. The File class has three constructors: File(String path), File(String path, String name) and File(File dir, String name). The File class also implements many important methods. The following table lists these methods and declares what they return.
Method |
Returns |
delete |
boolean |
canRead |
boolean |
canWrite |
boolean |
exists |
boolean |
isDirectory |
boolean |
renameTo |
boolean |
long lastModified |
long |
long length |
long |
getName |
String |
getParent |
String |
getPath |
String |
The RandomAccessFile class is more powerful than the FileInputStream and FileOutputStream classes. It implements reading and writing methods for all the primitive types. It has two constructors that are as followed: RandomAccessFile(String name, String mode) and RandomAccessFile(File file, String mode). The mode parameter indicates whether the RandomAccessFile object is used for reading ("r"), or reading/writing ("rw"). There are many powerful methods implemented by RandomAccessFile.
The following table lists some of these methods and what they accept.
Method |
Accepts |
skipBytes |
(int) |
getFilePointer |
() |
seek |
(long pos) |
read |
() |
read |
(byte b[], int off, int len) |
readType |
() |
write |
() |
write |
(byte b[], int off, int len) |
length |
() |
close |
() |
This class is used to break up a stream into individual tokens. The StreamTokenizer class implements methods used to define the lexical syntax of tokens. This technique of processing streams into tokens is perhaps most commonly used in writing compilers.
The Java Beans Component Library is a powerful API that extends the JDK 1.1. The JBCL package includes UI components, database connectivity components, and specialized I/O components. The components in the JBCL can be broken up into the following categories: Controls, Dataset components, I/O components, Layout managing components, Model components, View components and Utility components
The borland.jbcl.control package contains various UI components such as containers, dialogs, and controls. Many of the components in this package are basically enhanced versions of their AWT counterparts. They differ from equivalent AWT components in that they are composite, based on the model-view architecture (covered later), data-aware and allow sharing of data among multiple UI components. Some of the classes defined in this package are:
Class |
Component |
ButtonBar |
|
ButtonControl |
|
CheckBoxControl |
|
CheckBoxPanel |
|
ListControl |
|
ChoiceControl |
|
GridControl |
|
StatusBar |
|
TextAreaControl |
|
TextFieldControl |
|
The borland.jbcl.dataset package supports database connectivity in JBuilder. It defines classes that provide access to different types of data sources. Some of the functionality its classes implement includes logging on to remote data servers, retrieving data through queries and stored procedures, navigating and editing data locally and copying the local data set to the original source. Some of the classes defined in the dataset package are DataBase, DataSet, StorageDataSet, ProcedureDataSet, QueryDataSet, DataFile, ReadRow, ReadWriteRow, RowFilterResponse, RowStatus and QueryResolver.
The borland.jbcl.io package contains specialized classes for handling input and output. Some of the classes included in this package are AsciiInputStream, AsciiOutputStream, EncodedInputStream, EncodedOutputStream, SimpleCharInputStream and SimpleCharOutputStream.
The borland.jbcl.layout package contains layout managing and constraint classes. These classes provide various methods for defining how components are laid out in container classes. The containers in the Component Palette use a default layout. This default layout can be changed to one of the classes defined in the Layout package. Some of the layout classes are GridBagConstraints, PaneConstraints, XYConstraints and XYLayout.
The borland.jbcl.model package contains interfaces and classes used for building components based on the model-view architecture. The model-view approach encapsulates different functionality into separate objects. These objects include: a object that contains the data (a model), a object responsible for UI-based events and functionality (a view), a object that manages the visual display of different data types (a view manager), and a object that actually displays the data (a painter).
When a model object needs to display a certain data type, the view object is notified. The view object gets the data object from the model and passes on to the view manager. The view manager determines the type of the data, and returns to the view the proper painter object. The view calls on the painter, which finally displays the data in the model object.
The following table lists the four model types.
Model Type |
Description |
Singleton |
A single data item used by a checkbox component |
Vector |
A list of data items used by a list control |
Matrix |
A table of data items used by a grid control |
Graph |
A tree of data items used by a tree control |
The model package provides two interfaces used for each of these model types. One interface provides read-only access to the data. The other interface provides read and write access to the data. This package also contains several classes that provide the functionality needed for the four model types, two view manager classes, and several item painter classes.
The borland.jbcl.util package contains various utility classes and constants. The classes can be categorized as Two dimensional alignment managing classes, Event-dispatching classes, Exception-related classes, I/O classes, Multicaster classes or Timer classes.
Multicaster classes provide an efficient way for beans to manage their events. A multicaster class uses an array instead of a vector to keep a list of Event Listener objects. It also implements efficient methods for dispatching events, and for checking whether a particular Event Listener exists. These multicaster class types include the ActionMulticaster, EventMulticaster, FocusMulticaster, ItemMulticaster, MouseMotionMulticaster and the MouseMulticaster.
The JDK 1.1 has support for only three types of data structures: Vector, Stack, and Hashtable. The JDK's support for various algorithms, such as sorting and filtering, is not very strong. The freely distributed Java Generic Library (JGL) can overcome these shortcomings. Some of the JGL's most beneficial features include support for Serialization, multi-thread safe, optimal performance, 11 data structures, 40 algorithms and it is completely compatible with the JDK 1.1.
The JGL also includes a several data structures. The following table defines each data structures.
Data Structure |
Description |
Array |
Linear, contiguous storage |
Deque |
Linear, non-contiguous storage; inserts elements at both ends |
Dlist |
Doubly-linked list |
Slist |
Singly-linked list |
HashMap |
Stores mappings using a hash table |
OrderedMap |
Stores mappings in order |
HashSet |
Stores a set using a hash table |
OrderedSet |
Stores a set in order |
PriorityQueue |
Pops elements in a sorted order |
Queue |
First-in, first-out (FIFO) data structure |
Stack |
First-in, last-out (LIFO) data structure |
The JGL also contains several types of algorithms. The following table describes each algorithm in detail.
Algorithm |
Description |
Applying |
Applies a function to all elements of a sequence |
Copying |
Copies a sequence |
Filling |
Fills a sequence with a single element |
Filtering |
Filters a sequence |
Finding |
Finds an element based on a condition |
Heap |
Manipulates a heap |
Replacing |
Replaces an element |
Reversing |
Reverses a sequence |
Set operations |
Performs union, intersection, inclusion, and difference |
Shuffling |
Shuffles a sequence randomly |
Sorting |
Sorts a sequence |
Swapping |
Swaps elements or sequences |
In addition to containers and algorithms, the JGL defines a number of interfaces and exceptions. It also contains iterators that support different ways of iterating through the containers and function objects.
What was covered in this chapter: