Chapter 12

The Java I/O and Utility Class Libraries


CONTENTS


This chapter continues our adventure of exploring the Java packages. Two packages are covered here: java.io and java.util. The java.io package contains interfaces, classes, and methods that facilitate input and output of data to applets and applications. The java.util package is a mixed bag of utilities useful in writing Java programs. Its classes are used to manipulate dates, create hash tables, and directly manipulate stacks of objects, among other things.

Introduction to the java.io Package

The Java input and output package-java.io-is used to implement streams. Streams are covered in Chapter 8, "Tying It All Together: Threads, Exceptions, and More;" refer to that chapter if you have difficulty with some of the examples or information in this chapter. Here I cover the classes and methods that manipulate streams.

Streams are used to move groups of data from point A to the Java application or from the Java application to point B. At its simplest, transmitted data is in the form of a stream of bytes without formatting or identification. It is up to the Java application to reformat and reinterpret it in such a way that the data makes sense to the application.

Data traditionally has been stored in files on a disk somewhere. Moving data was a simple matter of opening a file, copying the data to memory, copying the needed portions from memory, and moving the data from memory to a new disk location under a new filename on a system that controlled all the steps of the process.

That scenario is certainly not the case these days. Data can be generated by users on a local network and loaded on a distributed system. It can come from just about any system in the world over the Internet; and from disk, tape, CD-ROM, and so on. The source of the data may be running any number of operating systems, which means that the data might be in a format foreign to the system running the Java application. Therefore, Java must be able to interpret data in many formats from many sources. It must be adaptable enough to handle this data without requiring code changes if the data stream origin changes. The classes and methods in java.io help make this happen.

How Java's I/O System Works

Remember that the whole idea behind Java is to provide a non-platform-specific application language. This includes the movement and interpretation of data. The classes and methods in the java.io package allow great flexibility in sending and receiving data.

Java provides control over the flow of input streams using mark and reset (discussed in Chapter 8). However, not all forms of Java I/O allow the use of mark and reset.

The following sections discuss the various ways to move data in Java. Pick the one that best suits your needs, because each has advantages and disadvantages.

Java Input Streams

Input streams are streams of data that arrive from a source and are loaded into Java. Java doesn't care about the data's origins; it only cares about the data being available to send to the Java application. All the input stream classes throw the exception IOException. (Refer to the section in Chapter 8 called "Exceptions" for more information.)

All the input stream classes are based on the abstract class InputStream. The next section discusses the methods associated with that class.

The InputStream Class

InputStream is the abstract class that contains all the possible types of inputs to Java. Its subclasses use most or all of its methods. These methods are discussed in the "Input Streams" section of Chapter 8, but let's go over them again.

read

All of InputStream's subclasses can read data from an input stream. These reads are based on blocking, which allows the Java application to wait if the input stream is not continuous.

skip

The skip method is used to bypass a fixed number of bytes of an input stream. This method is available to all of InputStream's subclasses.

close

The close method is used to shut down an input stream that is no longer being used. Files are closed automatically when the Java garbage collector finalizes and closes them. However, it is important to manually close the stream if the file is going to be opened again soon. You can't know exactly when the garbage collector will finally close the stream, and Java will not reopen an already open file.

available

The available method is used to determine whether the data is ready to be sent. Remember, the program will block if the data is not ready but a read request has been sent. If you put the read request in a separate thread, it won't matter if the block occurs; in that case it is not necessary to use available. Make sure, however, that this makes sense in the application. Using separate threads is not advisable if the whole application relies on the data to be sent.

The available method is not available for all subclasses of InputStream.

mark, markSupported, and reset

The mark method marks a point in an input stream to return to later. The markSupported method makes sure that the type of input stream allows mark. The reset method returns to the point marked by mark. These methods are not supported in all the subclasses of InputStream.

More on the InputStream Class and Its Subclasses

The subclasses of InputStream are ByteArrayInputStream, FileInputStream, FilterInputStream, PipedInputStream, SequenceInputStream, and StringBufferInputStream. Each is discussed in detail in the following sections.

The FilterInputStream Class

FilterInputStream has four subclasses: BufferedInputStream, DataInputStream, LineNumberInputStream, and PushbackInputStream. These subclasses work in a unique way with FilterInputStream: They allow it to control the flow of a file to them. FilterInputStream does nothing by itself.

Think of FilterInputStream as providing the structure of the pipe for the flow of data while its subclasses chop the data up into small, usable bits. Picture FilterInputStream as the a pasta maker that squeezes out the dough while the subclass turns it into spaghetti or fettucine or lasagna noodles. The data is the dough. It is also possible to change the "mold," or subclass, midstream, just as you would change the form on the pasta maker. However, you don't have to stop the machine while changing the form; the data still flows through.

FilterInputStream's subclasses are widely used in writing Java programs. Following is a discussion of the subclasses. (Read the online documentation for a full list of each subclass's capabilities.)

The BufferedInputStream Class
BufferedInputStream is a subclass of FilterInputStream. It implements all the methods defined in the superclass InputStream.

Note
BufferedInputStream is the only class that uses mark and reset correctly.

Another key attraction to using BufferedInputStream is that it creates a read buffer, which is an array of bytes. This read buffer smooths out the stream of data so that reading can be continuous, as opposed to waiting for the input to reach the stream. The size of the buffer and the size of the reads can be controlled through BufferedInputStream's methods. Listing 12.1 is an example of BufferedInputStream and BufferedOutputStream.

Listing 12.1. An example of BufferedInputStream and BufferedOutputStream.
/* Notes:

      This class implements buffered file input/output streams by extending
      class BufferedInputStream.  This is not a complete implementation of all
      of FileInputStream (not enough constructors, or getFd()).

      The main example simply copies the first 128 bytes of file one to
      file two, then resets file one to the beginning and proceeds to copy
      all of file one to file two.  This has no practical purpose, but does
      demonstrate that a BufferedFileInputStream class has been created that
      implements mark and reset, which FileInputStream does not support.

      Syntax:
         MyMain <source file> <dest file>
*/

import java.io.*;

public class MyMain {

   public static void main (String args[]) {

      // declare buffered file input stream
      BufferedFileInputStream bfis;
      // declare buffered file output stream
      BufferedFileOutputStream bfos;

      // declare buffer
      byte buf[];
      int cnt;

// create 1024 byte buffer
      buf = new byte[1024];
      try {
         //create buffered file input stream using first argument
         //on command line
         bfis = new BufferedFileInputStream(args[0]);
         //create buffered file output stream using second argument
         //on command line
         bfos = new BufferedFileOutputStream(args[1]);
         // mark beginning of file to be buffered for 128 bytes
         bfis.mark(128);
         // read 128 bytes from source file
         bfis.read(buf,0,128);
         // write buffered bytes to dest file
         bfos.write(buf,0,buf.length);
         // reset to beginning of file
         bfis.reset();
         // loop through source file reading 1024 bytes at a time
         while (bfis.read(buf,0, 1024) != -1) {
           // write buffer to dest file
           bfos.write(buf, 0, buf.length);
         }

         // close source file
         bfis.close();
         // close source file
         bfos.close();

      }
      // catch file not found errors (do nothing)
      catch (FileNotFoundException e){}
      // catch IO errors (do nothing)
      catch (IOException e){}
   }
}

// declare new class that extends buffered input stream
class  BufferedFileInputStream extends BufferedInputStream {

// declare file input stream
   FileInputStream f;

// override buffered input stream constructor
   public BufferedFileInputStream(String filename) throws IOException {
      // create buffered input stream using file input stream
      super(new FileInputStream(filename));
   }
}

// declare new class that extends buffered output stream
class  BufferedFileOutputStream extends BufferedOutputStream {

   // override buffered output stream
   public BufferedFileOutputStream(String filename) throws
   IOException {   
      // create buffered output stream using file output stream constructor
      super(new FileOutputStream(filename));
   }
}

The DataInputStream Class
DataInputStream is a subclass of FilterInputStream. It also implements the DataInput interface. Look at the following list of methods in DataInputStream, and the major use of the class becomes obvious (note that these same methods are used by the class RandomAccessFile):
readBoolean
readByte
readChar
readDouble
readFloat
readInt
readLine
readLong
readShort
readUTF
readUnsignedByte
readUnsignedShort
These methods are used to read primitive data types. Remember primitive data types from Chapter 6, "Fundamentals of the Java Language"? This class interprets primitive data types across platforms and makes them accessible to Java.
The LineNumberInputStream Class
LineNumberInputStream is a subclass of FilterInputStream. This class keeps track of line numbers, which could be used to mark and reset data streams. LineNumberInputStream also can be used to nest the handling of input streams so that it is possible to simultaneously read the stream for other purposes and keep track of line numbers.
The PushbackInputStream Class
PushbackInputStream is a subclass of FilterInputStream. It creates a one-byte input buffer that allows the input stream to retreat one byte after it has been read. This makes it possible to test the next byte before taking action. Listing 12.2 is an example of PushbackInputStream.

Listing 12.2. An example of PushbackInputStream.
/* Notes:

      This class implements a word search program. A word is defined as a
      character string.  It considered a word instead of a substring if the
      word is followed by a space.  The algorithm works its way through the
      input stream.  If it actually finds a word, it pushes the space
      that delimited the word back on the stream so that it can be processed
      if needed by other algorithms.

      The main example accepts a string to search for and a filename to
      search in as command line arguments.  It will print "Word found" if
      the word is successfully found, and "Word not found" if it does not.

      Syntax:
         MyMain <string to search for> <file to search>
*/

import java.io.*;

public class MyMain {

   public static void main (String args[]) {

      // declare FindWord object
      FindWord f;

      try {
         // create FindWord object by passing it a FileInputStream object.
         //File opened in second argument on command line.
         f = new FindWord(new FileInputStream(args[1]));
         
         // find word specified as first argument on command line.
         if (f.find(args[0]))
            // print out found message if word is found
            System.out.println("Word found");
         else
            // print out not found message if word not found
            System.out.println("Word not found");
      }

      // handle file not found exceptions
      catch(FileNotFoundException e) {
         System.err.println("ERROR: File not found");
         System.err.println("   file = " + args[1]);
         System.exit(2);
      }
   }
}

// declare class FindWord
class  FindWord {

   // declare PushbackInputStreamObject
   PushbackInputStream pbs;

   // declare FindWord constructor
   public FindWord(InputStream in) {
      // create pushback stream using passed in input stream
      pbs = new PushbackInputStream(in);
   }

   // declare method to find word in stream
   public boolean find (String word) {
      // declare buffer to be used in stream reads
      byte buf[];
      int i = 0;
      // declare booleans for word found and end-of-file
      boolean found, EOF;
      // create one byte array needed by stream read
      buf = new byte[1];
      // initialize word found variable to false
      found = false;
      // initialize end-of-file variable to false
      EOF = false;

      // declare string buffer so a space can be appended to search word
      StringBuffer sbWord;
      // create string buffer initialized to search word followed by a space
      sbWord = new StringBuffer(word + " ");

   // label for labeled continue inside loop
   startOver:
      // loop waiting for word found or end of file
      while ((! found) && (! EOF)) {

         // loop for matching search word to chars in input stream
         for (i=0; i < sbWord.length(); i++) {
            try {
               // read char from stream checking for EOF
               if (pbs.read(buf,0,1) == -1) {
                  // set EOF boolean if EOF reached
                  EOF = true;
               }

               // compare stream char to search word char
               if ((char)buf[0] != sbWord.charAt(i)) {
                  // if different continue in OUTER while loop
                  continue startOver;
               }
            }
            // handle IO exceptions
            catch(IOException e){
               // exit application if IO error occurred
               System.exit(3);}

         }
         // word found if this point reached, push space back on stream
         try {pbs.unread(buf[0]);}
         // handle IO exceptions
         catch(IOException e){
               // exit application if IO error occurred
               System.exit(4);}
         // return true since word found
         return(true);
      }
      // return false since word not found
      return(false);
   }
}

The ByteArrayInputStream Class

The ByteArrayInputStream class extends the InputStream class. This class is used to create an input stream from a buffer. The input stream is accessed in the number of bytes set by the programmer. The flow of data to the input stream is controlled by three variables:

ByteArrayInputStream allows use of the reset method, which resets the input stream back to the beginning. There is no mark method to mark a specific place in the stream.

The FileInputStream Class

The FileInputStream class extends the InputStream class. This class allows Java to read files. This will work only with sequential files, not with hash tables or indexed files. The file itself must be accessible to Java.

Be sure to explicitly close the file input stream if there are to be subsequent accesses to it after the end of file has been reached. mark and reset are not available to FileInputStream. The only way to return to a position is to skip to it and then read to find a desired piece of data. There is no way to know exact positions in the file directly with FileInputStream.

Refer to the section in Chapter 8 titled "Input Streams" for an example of the use of FileInputStream.

The SequenceInputStream Class

SequenceInputStream allows multiple files to be read in sequence and converted into a single stream. The first input is read through to the end of file, then the next file is read, and so on. The output is a byte array. The only methods available are read and close.

One reason to use SequenceInputStream is to receive multiple inputs, create one input stream, and pass the stream off to another class that has more data-manipulation methods available.

The StringBufferInputStream Class

StringBufferInputStream is used to create an input stream from a buffer. The input stream is an array of string characters. The flow of data to the input stream is controlled by three variables:

This is similar to ByteArrayInputStream; the only difference is that ByteArrayInputStream inputs the stream as an array of bytes, whereas StringBufferInputStream creates the input stream as an array of string characters. With StringBufferInputStream, you can use the reset method, which resets the input stream to the beginning. There is no mark method to mark a specific place in the stream.

Java Output Streams

Once again, refer to Chapter 8 for a detailed discussion of output streams. I will do only a quick review here. This section discusses the java.io package and its output stream classes.

Output streams are data streams generated by a Java application or applet for use elsewhere. There are several ways to format the data for output: It can be piped, buffered, printed to a screen, stored in an array of bytes, or output to a file. Output stream classes perform the output duty.

All the output stream classes are based on the abstract class OutputStream. The next section discusses the methods associated with OutputStream.

The OutputStream Class

OutputStream is the abstract class that contains all the possibilities of output from Java. Its subclasses are BufferedOutputStream, ByteArrayOutputStream, DataOutputStream, FileOutputStream, FilterOutputStream, PipedOutputStream, and PrintStream.

OutputStream's subclasses use most or all of its methods. These methods are discussed in Chapter 8, but a quick review never hurts.

write
The write method is used to write data to a stream. It varies for each class in its output. All write methods are the same in that they block, or wait, for the receiving device to catch up. All write methods also throw the exception IOException.
See the section titled "Input Streams" in Chapter 8 for an example of write.
flush
The flush method is used to force any data buffered for the output stream to be written to the output device. This is the only way to override a block. This also may be the only way to write data if the receiving device requires it.
close
The close method closes an output stream. It is identical in logic to the close used for input streams. The Java garbage collector schedules a cleanup to the output streams when an applet or application closes. Be sure to manually call close if the application can't wait for the garbage collector.

The FilterOutputStream Class

FilterOutputStream has three subclasses: BufferedOutputStream, DataOutputStream, and PrintStream. These subclasses work in a unique way with FilterOutputStream; they allow it to control the flow of a file to them. FilterOutputStream does nothing by itself. (This is the output equivalent to FilterInputStream.)

Following is a discussion of the subclasses. Read the online documentation for a full list of each subclass's capabilities.

The BufferedOutputStream Class

BufferedOutputStream implements all the methods defined in superclass OutputStream. BufferedOutputStream uses flush and can create a nested stream to allow other output streams to flush.

Another key attraction to using BufferedOutputStream is that it creates a write buffer in the form of an array of bytes. This write buffer smooths out the stream of data so that writing can be continuous, as opposed to waiting for the output to reach the stream. The size of the buffer and the size of the writes can be controlled through BufferedOutputStream's methods.

The DataOutputStream Class
DataOutputStream also implements the DataOutput interface. It is the inverse of DataInputStream and is used to write primitive data types. Following is a list of associated methods:
writeBoolean
writeByte
writeChar
writeDouble
writeFloat
writeInt
writeLine
writeLong
writeShort
writeUTF
writeUnsignedByte
writeUnsignedShort
DataOutputStream is used with BufferedInputStream to pull and push primitive data types across platforms.
The PrintStream Class
PrintStream is used to write formatted data to the user's screen. PrintStream is the actual class used to perform the printing in methods, like this:
System.out.print()
System.out.println()
It is possible to do autoflushing with PrintStream. The two constructors to use with PrintStream are
PrintStream(OutputStream)-Creates a new PrintStream
PrintStream(OutputStream, boolean)-Creates a new PrintStream, with autoflushing
PrintStream formats primitive data types for display to the screen. The following methods are used:
print(Object)
print(String)
print(char[])
print(char)
print(int)
print(long)
print(float)
print(double)
print(boolean)
println()
println(Object)
println(String)
println(char[])
println(char)
println(int)
println(long)
println(float)
println(double)
println(boolean)

See Listing 12.3 for an example of the use of PrintStream.


Listing 12.3. An example of the use of PrintStream.
/* Notes:

      This class implements a generic write class that can write any arbitrary
      object type to a file.  The constructor is used to open the file.  The
      resulting output stream is used as the constructor for the PrintStream
      class.  PrintStream is used because it can write arbitrary objects.
      PrintStreams implicitly use "toString" methods within objects to
      convert objects to strings.

      An arbitrary class called PhoneInfo is created simply for the purpose of
      demonstrating PrintStream's ability to write any type of object.

      The main example simply populates a PhoneInfo array and then uses the
      WriteObjToFile class to actually write the information to the file.  The
      filename to write to is specified on the command line.

      Syntax:
         MyMain <dest file>
*/

import java.io.*;

public class MyMain {

   public static void main (String args[]) {

      int i;
      // declare WriteObjToFile object
      WriteObjToFile w;
      // create info to populate phone info database
      String[] s =  {"Roy Rogers", "(111) 111-1111",
                     "Jesse James", "(222) 222-2222",
                     "Wyatt Earp", "(333) 333-3333"};
      // declare PhoneInfo array
      PhoneInfo[] p;

      // create PhoneInfo array
      p = new PhoneInfo[s.length/2];
      // loop through PhoneInfo array creating phoneinfo objects
      for (i=0; i<s.length; i+=2) {
         p[i/2] = new PhoneInfo(s[i],s[i+1]);
      }
      try {
         // create WriteObjToFile object
         w = new WriteObjToFile(args[0]);
         // loop through phone database passing objects to WriteObjToFile
         for (i=0; i<p.length; i++) {
            w.write(p[i]);
         }
      }
      // catch IO exceptions
      catch(IOException e) {
         System.err.println("ERROR: IO Exception");

         System.exit(2);
         }
   }
}

// declare PhoneInfo class
class PhoneInfo {

   // declare information variables
   String name, phoneNum;

   // declare PhoneInfo constructor
   public PhoneInfo(String name, String phoneNum) {
      // initialize name based on constructor argument
      this.name = new String(name);
      // initialize phone number based on constructor argument
      this.phoneNum = new String(phoneNum);
   }

   // declare toString method for returning string representation of instance
   public String toString() {
      // return string
      return("Name: " + name + "\nPhone Number: " + phoneNum);
   }
}

// declare WriteObjToFile class
class  WriteObjToFile {

   // declare PrintStream object
   PrintStream ps;

   // declare constructor
   public WriteObjToFile(String filename) throws IOException {
      try {
         // create printstream using FileOutPutStream as the stream to write to
         ps = new PrintStream(new FileOutputStream(filename));

      }
      // handle file not found errors
      catch (FileNotFoundException e) {

         System.err.println("ERROR: File not found");
         System.err.println("   File = " + filename);
         System.exit(2);
      }
   }

   // declare method to write arbitrary objects
   public void write (Object o) {

      // write object to output stream
      ps.println(o);

  }
}

The ByteArrayOutputStream Class

ByteArrayOutputStream, the counterpart of ByteArrayInputStream, is used to create an input stream from a buffer-that is, it pushes Java output into a buffer. The buffer grows as needed.

The flow of data to the output stream is controlled by two variables:

With ByteArrayOutputStream, you can use the reset method, which resets the output stream buffer so that the space of the buffer is not released and can be reused. There is no mark method to mark a specific place in the stream.

The FileOutputStream Class

FileOutputStream, the counterpart to FileInputStream, enables Java to write to files. The file and directory must be accessible to Java, which is a security issue.

Warning
Be sure to explicitly close the file output stream if there are to be subsequent accesses to it after the end of file has been reached. Otherwise, the file will not be properly terminated with an end of file marker and you will get errors.

The only methods available to FileOutputStream are the following:

close()-Closes the stream
finalize()-Closes the stream when garbage is collected
getFD()-Returns the file descriptor associated with this stream
write(int)-Writes a byte of data
write(byte[])-Writes an array of bytes
write(byte[], int, int)-Writes a subarray of bytes

The PipedOutputStream Class

PipedOutputStream supports moving data to and from threads that are piped together using the PipeInputStream. Because PipedOutputStream is connected to the PipeInputStream, the two must work together.

The methods associated with these classes are

close()-Closes the stream
connect(PipedInputStream)-Connects this output stream to a receiver
write(int)-Writes a byte
write(byte[], int, int)-Writes a subarray of bytes

Miscellaneous I/O Classes

The java.io package includes two miscellaneous I/O classes for dealing with files: File and RandomAccessFile.

The File Class

The File class is used to represent a filename on a host system. It cannot read or write to the file, but it can get information about the file such as its name, path, and so on. It works as an abstract way of dealing with a platform-specific file.

The RandomAccessFile Class

RandomAccessFile implements both DataInput and DataOutput interfaces. It combines both input and output access to a file within the security confines of Java. The numerous methods associated with it read and write primitive data types, close the file, and locate the file. RandomAccessFile also allows skip.

The java.util Package

The java.util package is sort of a catch-all for handy utilities. It contains the following classes:

BitSet
Date
Dictionary
Hashtable
Observable
Properties
Random
Stack
StringTokenizer
Vector

As you can see, this is certainly a mixed bag of classes. Read on for a description of each one. See the online documentation for details on all the methods associated with each class.

The Bitset Class

The Bitset class creates a set of bits that can grow as needed. It has methods that can manipulate the bits in the set. Listing 12.4 is an example of how to use Bitset.


Listing 12.4. An example using Bitset.
/* Notes:
   This class does 7 bit even parity generation and checking.
*/
import java.util.*;
public class MyMain {

   public static void main (String args[]) {

      Parity p;                        // declare parity object
      p = new Parity((byte)100);       // create parity object
      p.genParity();                   // generate parity bit
      if (p.chkParity())               // check for parity error
         System.out.println("no parity error");
      else
         System.out.println("parity error");
   }
}

class  Parity {                        // declare class parity

   BitSet b;                           // declare bitset object

   public Parity(byte data) {          // declare parity constructor
      int i;
      b = new BitSet(8);               // create new bitset object length 8
      for (i=0; i<7; i++) {            // copy input data bits to bitset
         if (((1 << i) & data) == 1) { // test if data bit is 1
            b.set(i);                  // set corresponding bitset bit
         }
      }
   }

   public void genParity() {           // method to generate parity bit
      int i, cnt;
      cnt = 0;
      for (i=0; i<7; i++) {           // loop through bitset counting data bits
         if (b.get(i)) {
            cnt++;
         }
      }
if ((cnt % 2) == 0)           // test if even or odd number data bits set
         b.clear(7);                   // even, clear parity bit
      else
         b.set(7);                     // odd, set parity bit
   }

   public boolean chkParity() {        // method to check for parity errors
      int i, cnt, parityBit;
      cnt = 0;
      for (i=0; i<7; i++) {           // loop through bitset counting data bits
         if (b.get(i)) {
            cnt++;                     // increment count of set data bits
         }
      }
      if (b.get(7))                    // get setting of parity bit
         parityBit = 1;                // parity bit set
      else
         parityBit = 0;                // parity bit not set
      if (((cnt %2) ^ parityBit) == 0) // check if parity bit set correctly
         return(true);                 // no parity error detected
      return(false);                   // parity error detected
   }
}

The Date Class

Date provides methods for examining and manipulating date and time. Time in Java is measured in milliseconds since January 1, 1970. Java attempts to handle time from the system with which it is interacting. UTC is Coordinated Universal Time, which seems to be the worldwide standard.

Note
The Date class does not work as documented in the Java API. Although you should be able to work with dates since 1900, dates before January 1, 1970 are generally not usable.

See Table 12.1 for a list of methods to see the full scope of this class.

Table 12.1. Methods available for the Date class.
Method
Purpose
UTC(int, int, int, int, int, int) Calculates a UTC value from YMDHMS
after(Date) Checks whether this date comes after the specified date
before(Date) Checks whether this date comes before the specified date
equals(Object) Compares this object against the specified object
getDate() Returns the day of the month
getDay() Returns the day of the week
getHours() Returns the hour
getMinutes() Returns the minute
getMonth() Returns the month
getSeconds() Returns the second
getTime() Returns the time in milliseconds since the epoch
getTimezoneOffset() Returns the time zone offset in minutes for the current locale that is appropriate for this time
getYear() Returns the year after 1900
hashCode() Computes a number that is used when storing objects in hash tables.
parse(String) Given a string representing a time, parses it and returns the time value
setDate(int) Sets the date
setHours(int) Sets the hours
setMinutes(int) Sets the minutes
setMonth(int) Sets the month
setSeconds(int) Sets the seconds
setTime(long) Sets the time
setYear(int) Sets the year
toGMTString() Converts a date to a String object using the Internet GMT conventions
toLocaleString() Converts a date to a String object, using the locale conventions
toString() Converts a date to a String object using the UNIX time conventions

It is important to remember that months start with 0, which is January, and end with 11, which is December. The days of the week also start with 0, which is Sunday, and go through 6, which is Saturday. Dates of the month are normal.

Note
Dates that are not within the normal range parameters are interpreted through consecutive iterations of the date range. For example, 32 January is recognized as 1 February.

Listing 12.5 is an example of the use of the Date class.


Listing 12.5. Using Date.
/* Notes:

      Java's time handling utilities in java.util do not seem to handle
      dates before 1970 even though it is documented as 1900.
      Usage: date specified on the command line as first argument:
         MyMain "18 Apr 1972"
*/

import java.util.*;

public class MyMain {

   public static void main (String args[]) {

BDay b;                    // declare birthday object;
      b = new BDay(args[0]);     // create birthday object initialized
                                 // to first date specified on command line
      b.printBDayInfo();         // print out resulting birthday information
   }
}

class  BDay {                          // declare new class birthday

   String sign;              // declare string object to hold Astrological sign
   Date d;
// declare date object to hold date of birth

   public BDay(String date) {          // declare constructor for this class
      Date c;                       // declare date object used for comparisons
      int year;                        // declare int to hold year of birth

      d = new Date(date);
// create new date object initialized to birthday
      year = d.getYear();              // get year from birth date
      while (true) {                   // loop finding Astrological sign
         c = new Date("20 Jan " + year);
// create comparison date of end of capricorn period
         if (d.before(c)) {
// check if birth date came before capricorn
            sign = new String("Capricorn");     // if so, set sign to capricorn
            break;                             &nbs p;// break out of while loop
         }
         c = new Date("19 Feb " + year);        // repeat above for aquarius
         if (d.before(c)) {
            sign = new String("Aquarius");
            break;
         }
         c = new Date("21 Mar " + year);        // repeat above for pisces
         if (d.before(c)) {
            sign = new String("Pisces");
            break;
         }
         c = new Date("20 Apr " + year);        // repeat above for aries
         if (d.before(c)) {
            sign = new String("Aries");
            break;
         }
         c = new Date("21 May " + year);        // repeat above for taurus
         if (d.before(c)) {
            sign = new String("Taurus");
            break;
         }
         c = new Date("21 Jun " + year);        // repeat above for gemini
         if (d.before(c)) {
            sign = new String("Gemini");
            break;
         }
         c = new Date("23 Jul " + year);        // repeat above for cancer
         if (d.before(c)) {
            sign = new String("Cancer");
            break;
         }
         c = new Date("23 Aug " + year);        // repeat above for leo
         if (d.before(c)) {
            sign = new String("Leo");
            break;
         }
         c = new Date("23 Sep" + year);         // repeat above for virgo
         if (d.before(c)) {
            sign = new String("Virgo");
            break;
         }
         c = new Date("23 Oct " + year);        // repeat above for libra
         if (d.before(c)) {
            sign = new String("Libra");
            break;
         }
         c = new Date("22 Nov " + year);        // repeat above for scorpio
         if (d.before(c)) {
            sign = new String("Scorpio");
            break;
         }
         c = new Date("22 Dec" + year);         // repeat above for sagittarius
         if (d.before(c)) {
            sign = new String("Sagittarius");
            break;
         }
         c = new Date("31 Dec" + year);         // catch end case for capricorn
         if (d.before(c)) {
            sign = new String("Capricorn");
            break;
         }
      }
   }

// declare method for printing birthdate information
   public void printBDayInfo() {
      Date t,c;
// declare date objects for todays date and for comparisons
      t = new Date();                           // obtain todays date
      t.setHours(0);
// set hours of todays date to zero
      t.setMinutes(0);
// set minutes of todays date to zero
      t.setSeconds(0);
// set seconds of todays date to zero
      c = new Date(t.getYear(), (d.getMonth()-1), d.getDate());
// create date object for this years birthday
System.out.println("You were born on: " + (d.getMonth()+1) + "/" +
      d.getDate() + "/" + d.getYear());     // print out birth date
      System.out.println("Your sign is:     " + sign);
// print out Astrological sign
      if (c.before(t))
// check if birthday already occurred this year
         System.out.println("Your birthday has already occurred this year");
// print out birthday already occurred
      else
         if (c.after(t))
// check if birthday is yet to come this year
            System.out.println("Your birthday has yet to occur this year");
// print out birthday is yet to come
         else
            System.out.println("TODAY IS YOUR BIRTHDAY!");
// if neither before today or after today, it must be today
   }
}

As you can see, there are numerous ways to use Date's methods to manipulate and examine dates.

The Dictionary Class

Dictionary is an abstract class. Its only current subclass is Hashtable. Dictionary is used to create a way of organizing objects. (See the following "Hashtable" section for the implementation of Dictionary.)

The idea is to access objects nonsequentially. There are keys to identify and access objects. Think of Dictionary as storing objects in a structure similar to an indexed file.

The Hashtable Class

Hashtable extends Dictionary and is used to map keys to values. The methods associated with Hashtable enable the placing, locating, and retrieving of objects that are used as keys. The Properties class extends Hashtable.

The Properties Class

Properties is a class that loads an input stream into a hash table. It also allows saves and gets from the hash table.

The Random Class

Random contains methods that create pseudo-random numbers. These numbers can be either Gaussian, Double, Float, Int, or Long. The seed, or starting point, can be reset anytime using the appropriate method.

The Vector Class

Vector is the abstract parent class for Stack. You will use the vector class to implement a growable array of objects.

The Stack Class

Stack extends the Vector class. It creates a last in, first out (LIFO) stack of objects. See Listing 12.6 for an example using Stack.


Listing 12.6. An example using Stack.
/* Notes:

      Either java or NT java will not accept "-" as a command line argument.
      The user must specify "--" to get the program to simply see "-".
      This program works by specifying an RPN string on the command line.
*/

import java.util.*;

public class MyMain {

   public static void main (String args[]) {

      // declare RPN calculator object
      RPncalculator c;
      // create RPN calculator passing command line arguments
      c = new RPncalculator(args);
   }
}

// declare new RPN calculator class
class  RPncalculator {

   // declare Stack object
   Stack s;
   // declare generic object
   Object o;

   // RPN Calculator constructor which also solves equation string passed to it
   public RPncalculator(String args[]) {

      int i,j;
      int[] num;
      // declare object to hold current arithmetic operator
      Character operator;
      // create new stack
      s = new Stack();
      // create two integer array to hold operands
      num = new int[2];

      // loop through equation strings
      for (i=0; i < args.length; i++) {
         // handle number format exceptions
         try {
            // try converting string to number and push on stack if successful
            s.push(new Integer(args[i]));
         }

         // string is not a number, must be an operator
         catch(java.lang.NumberFormatException e) {
            // convert string operator to character
            operator = new Character(args[i].charAt(0));
            // loop through operands operator will be used with
            for (j=0; j<2; j++) {

               // not enough operands on stack, generate error
               if (s.empty()) {
                  System.err.println("ERROR: stack is empty");
                  System.exit(2);
               }

               // extract operand from stack
               num[j] = ((Integer)s.pop()).intValue();
            }

            // switch based on operator
            switch (operator.charValue()) {

             // operator is "+", add operands
             case '+':
               s.push(new Integer(num[1] + num[0]));
               break;

             // operator is "-", subtract operands
             case '-':
               s.push(new Integer(num[1] - num[0]));
               break;

             // operator is "*", multiply operands
             case '*':
               s.push(new Integer(num[1] * num[0]));
               break;

             // operator is "/", divide operands
             case '/':
               s.push(new Integer(num[1] / num[0]));
               break;

             default:
               // unknown operator, print error message
               System.err.println("ERROR: invalid operator: " + operator);
               System.exit(3);
               break;
            }
         }
      }
      // value remaining on stack must be answer, print to screen
      System.out.println(((Integer)s.pop()).intValue());
   }
}

The StringTokenizer Class

The StringTokenizer class provides a set of methods for converting an input stream into a set of tokens. Methods are provided for specifying which characters make up a token, which characters serve as token delimiters, whether case should be considered, and whether numeric characters should be converted to numbers. The StringTokenizer class also understands the /* */ comment-naming conventions of C, C++, and Java as well as the // used in C++ and Java.

The StringTokenizer class specifies all the parts that determine a token and then retrieves a single token in its entirety piece by piece from an input stream. This can be used for anything from splitting a simple sentence into individual words to processing something as complex as Java source code. (See the online documentation for details of the individual methods.)

Summary

Java provides several ways to deal with moving data through its applications. The java.io package contains classes that handle the input and output of data. The data arrives in a stream and can be sliced and diced in various ways by the subclasses of the abstract classes InputStream and OutputStream.

The data stream may block, or wait, if the flow is not continuous. Both input and output classes provide buffers to smooth this flow. All input classes are able to skip a certain number of bytes forward in the stream; some classes can mark a spot and then reset the stream to that spot. Some classes store the data in byte arrays, and one class stores it in a character string array.

Data accessed or stored by Java must originate or be placed in an area that is Java accessible. Much of the Java security schema relies on the segmentation of Java data from other system data. Therefore, it would not be a good idea to put a copy of your password file in this area.

The java.io package doesn't care where data originates; it only cares about what the programmer wants to do with it. The data-manipulation classes enable Java to be truly non-platform-specific, even when manipulating data.

The java.util package contains classes that add functionality to Java. These classes include a date manipulator (Date) and a bitset creator and manipulator (Bitset). There are also classes that provide a mechanism for a hash table structure of an object file (Dictionary and Hashtable). There is a pseudo-random number generator (Random). Another class creates a stack of objects (Stack), and another turns a string of characters into tokens (StringTokenizer).