Richard G Baldwin (512) 223-4758, baldwin@austin.cc.tx.us, http://www2.austin.cc.tx.us/baldwin/

Core Java Classes, Input and Output Streams

Java Programming, Lesson # 60, Revised 10/11/98.

Preface
Introduction
Getting Started with I/O Streams
Hierarchy Diagrams for I/O Streams
Filtered Streams
Miscellaneous Classes
Interfaces
Reading and Writing Files using Streams
Implementing Pipes using Streams
Using Filtered Streams
Reading and Writing Memory Buffers using Streams
Creating Your Own Filtered Stream Classes
Reading and Writing Random Access Files
Reader and Writer Classes
Review

Preface

Students in Prof. Baldwin's Intermediate Java Programming classes at ACC are responsible for knowing and understanding all of the material in this lesson.

As of 1/9/98 the programs named files02.java and stream02.java still use deprecated APIs from JDK 1.0.2. However, the programs named SampProg151 and SampProg152 in the Review section at the end of the lesson show how to rewrite these two programs and eliminate the use of deprecated APIs.

The Java 1.1 class library provides two different types of I/O classes -- byte-oriented and character-oriented.

The two types of streams are organized into two separate class hierarchies, one consisting of the byte-oriented stream classes, and the other consisting of character-oriented stream classes. The classes within the two hierarchies have the same names, except for their suffix.

The byte-oriented stream classes end in either InputStream or OutputStream, while the character-oriented stream classes end in either Reader or Writer.

The two hierarchies are functionally almost identical, and they contain most of the same subclass specializations.

Most of the programs in this lesson use InputStream or OutputStream. However, the two program mentioned above that illustrate how to eliminate deprecated APIs use the Reader and Writer classes for character-oriented data. Also, a good reference for learning more about this topic is the article entitled Use the two "R"s of Java 1.1 -- Readers and Writers - JavaWorld - November 1997.

In September of 1998, a new section was added near the end of this lesson that deals with the new Reader and Writer classes.  It contains one sample program that shows how to convert the sample program named files02.java into a character-stream program using Reader and Writer classes.

A good reference for learning more about this topic is the article entitled Use the two "R"s of Java 1.1 -- Readers and Writers - JavaWorld - November 1997.

Several new classes were added to JDK 1.1 that deal with I/O of entire objects. The names of the classes usually contain the word Object. Some of these classes are discussed in a subsequent lesson on object serialization.

Introduction

According to The Java Tutorial by Mary Campione and Kathy Walrath, http://java.sun.com/books/Series/Tutorial/java/io/index.html
 

"A stream is a flowing sequence of characters."

Usually the task is to move data from memory to an external device or vice versa, but not necessarily. Streams can also be used to move data from one part of memory to another part of memory just as well.

It has been said that through the use of streams, it is the responsibility of the computer to move bytes from one place to another without regard for the meaning associated with those bytes.

It is the responsibility of the programmer to assign meaning to the bytes.

For example, a group of 32 bytes could represent 32 pieces of graphic data, or 8 integer values; the stream I/O system doesn't usually know and doesn't care.

Only the programmer and the user know and care what the 32 bytes are meant to represent.

In other words, a stream is usually considered to be an abstraction for the capability to move bytes from a source to a sink.

We have previously seen examples of moving bytes from the keyboard (standard input device) to the computer's memory, and have seen examples of moving bytes from the computer's memory to the screen (standard output device).

We have also touched on the need to move bytes between disk files and the computer's memory.

In a later lesson, we will look at the requirement for and the techniques available to move bytes between the memory and a network.

All of these examples involve the use of streams.

The package named java.io contains a set of input and output stream classes that can be used to read and write data.

The InputStream class and OutputStream class are abstract superclasses that define the behavior for sequential input and output streams in Java.

The java.io package also provides specialized InputStream and OutputStream subclasses that are used for specialized types of input and output.

We will examine each of the classes, discuss how to use them, and how to subclass them for your own purposes.

Getting Started with I/O Streams

The Output Stream Class
The PrintStream Class
Reference Material
Standard Input

You are already familiar with the use of standard input, standard output, and standard error as provided by the System class.

For example, we have been using the following code fragments since the beginning of the Introductory course:
 

System.out.println("my output string");

While (System.in.read() != -1) ...

According to The Java Tutorial:
 

System.out refers to an output stream managed by the System class that implements the standard output system.

System.out is an instance (an object) of the PrintStream class defined in the java.io package. The PrintStream class is a subclass of OutputStream.

The OutputStream Class

According to Java in a Nutshell by David Flanagan, the OutputStream class is an abstract class which is the superclass of all output streams. It defines the basic output methods that all output streams provide. The following methods are defined in the OutputStream class.
 

close() - Closes the stream. 
flush() - Flushes the stream. 
write(int) - Writes a byte. 
write(byte[]) - Writes an array of bytes. 
write(byte[], int, int) - Writes a sub array of bytes.

.

The PrintStream Class

The Java specification, which is available on-line at JavaSoft, http://java.sun.com/ provides the following description for the PrintStream class:
 

A PrintStream adds functionality to another output stream -- namely, the ability to print representations of various data values conveniently. 

Two other features are provided as well. Unlike other output streams, a PrintStream never throws an IOException; instead, exceptional situations merely set an internal flag that can be tested by the checkError method. 

Optionally, a PrintStream can be created so as to "autoflush"; this means that after an array of bytes is written, or after a single byte equal to '\n' is written, the flush method is automatically invoked.

The following methods are included in the list of methods available to objects of the PrintStream class of which the out object in the System class is an instance.

These are the methods of the System.out stream object.
 

checkError() - Flushes the print stream and returns whether or not there was 
an error on the output stream. 
close() - Closes the stream. 
flush() - Flushes the stream. 

print(various argument types) - Prints the argument. 

println(various argument types) - Prints the argument. 
println(String) Prints a string followed by a newline. 

write(int) - Writes a byte. 
write(byte[], int, int) - Writes a sub array of bytes.

The println(String) method which we have used extensively in example programs in previous lessons is highlighted in boldface above. There are many other available methods as well.

Recall that when we first used System.out.println(String), we explained that we were invoking the println() method of the out object which is a class variable of the System class.

Since the out object is of type PrintStream, all of the methods listed above are available to the out object.

Because it is a class variable, we can access it and its methods without first instantiating an object of the System class.

Java in a Nutshell indicates that the PrintStream class extends a class known as FilterOutputStream. We will deal with filtering later in this lesson.

Note that the System class is not part of java.io. Rather, it is part of java.lang.

The hierarchy relative to the object named out is
 

  • Object is the class from which all other classes are derived 
    • OutputStream is a subclass of Object
      • FilterOutputStream is a subclass of OutputStream. 
        • PrintStream is a subclass of FilterOutputStream
          • System contains a class variable named out of type PrintStream

All classes in Java are subclasses of Object at some level. Each level of inheritance between the class named Object and the class variable named out adds methods and variables. The object named out contains all the methods and variables contributed by all of its ancestors.

Reference Material

Part of the difficulty of programming in Java is establishing and understanding class hierarchies as described above. Java in a Nutshell is an excellent reference in this regard. Another excellent reference is the Windows-compatible help file published by Bill Bercik at (site no longer valid).

The Java language Specification, the API Specification, and a lot of other useful information is also available at Sun's JavaSoft page http://java.sun.com/ in other formats.

You should avail yourself of one or more of these sources plus other good sources that you identify if you plan to program successfully in Java.

Standard Input

Now let's examine the following commonly-used code fragment with an eye toward the hierarchy.
 

While (System.in.read() != -1) ...

Here, we are invoking the read() method of the object referred to by in which is a class variable of the System class. The System class has three class variables:

As seen in an earlier lesson, the System class also has about a dozen class methods which can be invoked without a requirement to instantiate an object of the class.

The InputStream class (of which the object referenced by in is an instance) is a subclass of the Object class and has considerably fewer methods than the PrintStream class (of which the object referenced by out is an instance).

The InputStream contains three overloaded public versions of read(); the one shown above and two which deal with arrays of type byte. All three throw IOException.

Peter Norton's Guide to Java Programming indicates that the form of read() which takes no arguments attempts to read and return a single byte from the input stream, cast as an integer. If there is nothing more to be read, it returns a -1. The fact that the byte is returned cast as an integer needs to be taken into account when using this method.

The following description of the read() method which takes no parameters comes from JavaSoft's Java Language specification (emphasis added by this author).
 

The general contract of read is that it reads one byte from the input stream. The byte is returned as an integer in the range 0 to 255 (0x00-0xff). If no byte is available because the stream is at end of file, the value -1 is returned

This method blocks until input data is available, end of file is detected, or an exception is thrown. 

If the byte cannot be read for any reason other than end of file, an IOException is thrown. In particular, an IOException is thrown if the input stream has been closed

Similar material regarding the other overloaded versions of the read() method is also available in the Java Language Specification.

The hierarchy for the object in is (approximately)
 

  • Object is the class from which all other classes are derived 
    • InputStream is an abstract subclass of the class Object 
      • in is a class variable of type InputStream 

While we know that it is not possible to instantiate objects of abstract classes (such as InputStream), this structure suggests that it is possible to define class variables (such as in) which reference abstract classes, which in turn causes the class methods of the abstract class to become available by way of the class variable.

According to Java in a Nutshell, InputStream is the superclass of all input streams and it defines the basic input methods that all input stream classes provide.
 
 

Hierarchy Diagrams for I/O Streams

FileInputStream
FileOutputStream
PipedInputStream
PipedOutputStream
ByteArrayInputStream
ByteArrayOutputStream
SequenceInputStream
StringBufferInputStream

Most of the other classes in the java.io package derive from two classes: InputStream and OutputStream (also see the comment in the Preface section regarding readers and writers)

The InputStream class defines methods for

OutputStream defines methods for writing bytes or arrays of bytes to the stream.

Input and output streams are automatically opened when you instantiate an object of the proper type. Several example programs will be provided later which show the opening of input and output streams.

You can close an input stream or an output stream by using the close() method. Otherwise, it will be closed when the stream object is garbage collected.

Recall that objects become eligible for garbage collection when they are no longer referenced by an object reference variable but there is generally no guarantee that garbage collection will occur before the program terminates.

Both InputStream and OutputStream have several subclasses that implement specific input and output functions.

The hierarchy structure for InputStream is shown in the first of the following two diagrams.

The hierarchy structure for OutputStream is shown in the second diagram.
 

Object
        InputStream
                FileInputStream
                PipedInputStream
                FilterInputStream (abstract)
                        DataInputStream
                        BufferedInputStream
                        LineNumberInputStream
                        PushbackInputStream
                ByteArrayInputStream
                SequenceInputStream
                StringBufferInputStream

.
 

Object
        OutputStream
                FileOutputStream
                PipedOutputStream
                FilterOutputStream (abstract)
                        DataOutputStream
                        BufferedOutputStream
                        PrintStream
                ByteArrayOutputStream

A non-exhaustive description of some of the classes in the above diagram follows.

FileInputStream

This is a subclass of InputStream that reads data from a file specified by name or by a File or FileDescriptor object. While it is possible to read data using objects of this class, it is more common to use an object of type DataInputStream which is a type of FilterInputStream that allows you to read lines of text and Java primitive data types in a portable way.

You can create a DataInputStream by specifying the InputStream that is to be filtered in the call to the constructor.

FileOutputStream

This is a subclass of OutputStream that writes data to a file specified by name or by a File or FileDescriptor object. This is a low-level class. You would typically use a DataOutputStream or PrintOutputStream to filter output to a FileOutputStream. DataOutputStream is a subclass of FilterOutputStream that allows you to write Java primitive data types in a portable way.

You can create a DataOutputStream by specifying the OutputStream that is to be filtered in the call to the constructor.

PipedInputStream

This class is an InputStream that implements one-half of a pipe, and is useful for communication between threads. A PipedInputStream must be connected to a PipedOutputStream object, which may be specified when the PipedInputStream is created, or with the connect() method.

Data read from a PipedInputStream object is received from the PipedOutputStream to which it is connected.

PipedOutputStream

Implements the other half of a pipe (see PipedInputStream above). Similar connection requirement apply.

ByteArrayInputStream

An object of this class provides input data from a specified array of byte values. This is useful when you want to read data in memory as if it were coming from a file, pipe, or socket. (Similar to a StringBufferInputStream.)

ByteArrayOutputStream

Data that is written is stored in an internal byte array which grows as necessary, and can be retrieved using toByteArray() or toString().

The reset() method discards any data currently stored in the array and begins storing data from the beginning.

SequenceInputStream

Provides a way to concatenate the data from two or more input streams. Provides an interface to a sequence of InputStream objects. Data is read from the streams in the order in which the streams are specified.

StringBufferInputStream

Data comes from the characters of a specified String object. Useful to read data from memory as if it were coming from a file, a pipe, or a socket. (Similar to ByteArrayInputStream.)

Filtered Streams

DataInputStream
BufferedInputStream
LineNumberInputStream
PushbackInputStream
BufferedOutputStream
PrintStream
DataOutputStream

The following classes derive from FilterInputStream and FilterOutputStream which are both abstract classes. These classes define the interface for filtered streams which process data as it's being read or written.

DataInputStream

Allows you to read lines of text and Java primitive data types in a portable way. Possibly the most commonly used class for file input. A DataInputStream object provides facilities for reading bytes from an input source and interpreting specific character sequences as representing data of diverse types. Many methods for translating from byte sequences to data types are described in the Java Language Specification.

BufferedInputStream

Provides input data buffering which increases efficiency by reading in large amounts of data and storing them in an internal buffer.

LineNumberInputStream

Keeps track of the number of lines of data that have been read. The getLineNumber() method returns the current line number. The setLineNumber() method sets the line number of the current line and subsequent lines are numbered starting from that number.

PushbackInputStream

Implements a one-byte pushback buffer. The unread() method "pushes" one character back into the stream, which will be the first one read by the next call to a read() method.

BufferedOutputStream

Provides output data buffering which increases efficiency by storing values to be written in a buffer and actually writing them out when the buffer fills or when the flush() method is called.

PrintStream

A PrintStream adds functionality to another output stream -- namely, the ability to print representations of various data values conveniently. More detailed information regarding this class was provided earlier in this lesson.

Note that according to Java in a Nutshell, this method does not handle Unicode data. The class discards the top 8 bits of all 16-bit Unicode characters.

DataOutputStream

Provides facilities for converting data of diverse types into character sequences of specific formats that are then sent to some output stream.

Miscellaneous Classes

File
FileDescriptor
RandomAccessFile
StreamTokenizer

The java.io package contains other classes in addition to the stream classes.

File

Provides a platform-independent definition of file and directory names. Also provides methods useful for performing a number of utility operations on files. For example, you can create a File object for a file on the native file system and then query the object for information about that file (such as its full pathname).

FileDescriptor

Platform independent representation of a low-level handle to an open file or an open socket. According to the Java Language Specification
 

A FileDescriptor is an opaque representation of a connection to an actual file in a file system, or to a network socket, or to another source or sink of bytes. The main practical use for a file descriptor is to create a FileInputStream or FileOutputStream to contain it.

.

RandomAccessFile

Allows reading and writing of arbitrary bytes, text, and primitive Java data types from or to any specified location in a file. Not a subclass of InputStream or OtputStream. Rather RandomAccessFile is an entirely independent method for reading and writing data from and to files. A large number of methods are provided.

You may have noticed that unlike other languages, thus far, we have not made mention of an ability to append data to an existing file. In order to append data to an existing file in Java, you must treat it as a random access file.

StreamTokenizer

Performs lexical analysis of a specified input stream and breaks the contents of a stream into tokens.

Interfaces

DataInput
DataOutput
FilenameFilter

The java.io package defines three interfaces which are implemented by a variety of classes.

DataInput

Declares the methods required for streams that can read character data or Java primitive data types in a machine-independent format.

DataOutput

Declares the methods required for streams that can write character data or Java primitive data types in a machine-independent format.

FilenameFilter

Declares the accept() method that must be implemented by any object that filters filenames (selects a subset of filenames from a list of filenames).

Reading and Writing Files using Streams

To perform file I/O in Java, you must instantiate a stream object of the appropriate type and link it to a device.

As you can see from the following program, writing and reading sequential files in Java is fairly straightforward.
 

/* File files01.java Copyright 1997, R.G.Baldwin
This application illustrates writing and then reading a 
file one byte at a time.

The instantiation of a file stream object is illustrated 
using two different approaches:
1.  Using a literal string containing the name of the file.
2.  Using an object of type File containing the name of 
the file.

Also illustrates use of the methods of the File class to
inquire about the properties of the file:  absolute path 
and length.

The output from the program is:

Start the program and write a file
Get info on, read, and print the file
Path is C:\BALDWIN\Java\SampProg\Java\junk.txt
Length 4
Dick
End of program
**********************************************************/

import java.io.*;

class files01{
  public static void main(String[] args)
  {
    System.out.println(
                    "Start the program and write a file");
    try{
    //Instantiate and initialize an output file stream 
    // object using a string containing the name of the 
    // file.
    FileOutputStream outFile = 
                          new FileOutputStream("junk.txt");
  
    //Write four bytes to the file and close it
    outFile.write('D');
    outFile.write('i');
    outFile.write('c');
    outFile.write('k');
    outFile.close();
  }catch(IOException e){}

    System.out.println(
                  "Get info on, read, and print the file");
  
  //Instantiate an object of type file containing the name
  // of the file.
  File junkFile = new File("junk.txt");
  
  //Use the File object to get info about the file
  System.out.println(
                 "Path is " + junkFile.getAbsolutePath());
  System.out.println("Length " + junkFile.length() );
  
  //Now read and print the data in the file
  try{
    //Instantiate and initialize the stream object using 
    // the File object.
    FileInputStream inFile = new FileInputStream(junkFile);
    int data;
  
    //Read and print until eof indicated by -1.  read() 
    // method returns integer.  Must cast as char to print.
    // Otherwise, the numeric value of the byte is 
    // displayed.
    while( (data = inFile.read()) != -1)
      System.out.print((char)data);
    inFile.close();
  }catch(IOException e){}
  System.out.println(); //new line
  System.out.println("End of program");
  }// end main

}//end class files01 definition

Implementing Pipes using Streams

Pipes may be used to cause the output of one thread in a program to feed into the input of another thread. The input and output components of a pipe are implemented by PipedInputStream and PipedOutputStream.

The following application instantiates and runs two threads. One of the threads places integer data into a PipedOutputStream object which is connected to a PipedInputStream object. The other thread receives its input from the PipedInputStream object.

The thread which generates the integers implements a random time delay between the output of successive integers to allow the other thread an opportunity to gain control of the system. Otherwise, depending on the platform being used, the first thread might hog the system until it completes its task. (Some implementations of the JVM provide for time-slicing between threads of equal priority while other implementations do not.)

The thread which receives the integers terminates when it receives a -1 from the input object indicating end of file. Note that the end of file is generated automatically by the system when the thread that generates the integers completes its task.

A main function is used to instantiate the stream objects, instantiate the thread objects, and start the threads running.

There are a couple of interesting things to note about this program. First, it implements an operation very similar to the producer/consumer program in an earlier lesson on interrupts, but in a much simpler way.

Second, even though all three threads have finalizer methods that announce that the thread is being finalized (immediately prior to garbage collection) there is no finalizer message produced by the main thread.
 

/* File pipe01.java Copyright 1997, R.G.Baldwin
This Java application illustrates the piping of the output
from one thread to the input of another thread.

The SenderThread sends the integers 0, 1, 2, and 3 with 
pauses in between.  The ReceiverThread receives and sums 
the integers.

The actual sequence of data in the output depends on the 
values obtained from the random number generator used to 
produce pauses.  

Tested using JDK 1.1.3 under Win95.

For one particular run, the output was as 
follows:

Starting SenderThread
Sending 0
Starting ReceiverThread
Received 0
Sum = 0
Sending 1
Received 1
Sum = 1
Sending 2
Sending 3
Received 2
Sum = 3
Received 3
Sum = 6
SenderThread done
ReceiverThread done, sum = 6
Finalizing sender thread
Finalizing receiver thread
**********************************************************/
import java.io.*;

class SenderThread extends Thread{
  //Piped output stream obj init by constructor
  PipedOutputStream pos; 

  //-----------------------------------------------------//
  
  //Constructor
  public SenderThread(PipedOutputStream posIn){
    pos = posIn; //save piped output stream object
  }//end SenderThread constructor
  //-----------------------------------------------------//

  public void run(){ //this method runs as a thread
    System.out.println("Starting SenderThread");
    try{
      for( int i = 0; i < 4; i++){
        //write a byte to the piped output stream
        pos.write(i); 
        System.out.println("Sending " + i);
        try{//sleep for awhile
          sleep((int)(Math.random() * 1000)); 
        }catch(InterruptedException e){}
      }//end for loop
    }catch(IOException e){}
    System.out.println("SenderThread done");
  }//end run() method
  //-----------------------------------------------------//
  
  public void finalize(){
    System.out.println("Finalizing sender thread");
  }//end finalize
  //-----------------------------------------------------//

}//end SenderThread class definition
//=======================================================//


class ReceiverThread extends Thread{
  //piped input stream obj init by constructor
  PipedInputStream inputStream; 
  int sum = 0, inData = 0; //working variables
  //-----------------------------------------------------//
  
  //Constructor
  public ReceiverThread(PipedInputStream inStream){
    //save piped input stream object
    inputStream = inStream; 
  }//end ReceiverThread constructor
  //-----------------------------------------------------//

  public void run(){ //this method runs as a thread
    System.out.println("Starting ReceiverThread");
    try{
      //read the first byte as an integer
      inData = inputStream.read(); 
      System.out.println("Received " + inData);
      while (inData != -1){ 
        //read until integer -1 signals no more data
        sum += inData; //accumulate the sum
        System.out.println("Sum = " + sum);
        //read next byte as an integer
        inData = inputStream.read(); 
        System.out.println("Received " + inData);
      }//end while loop
    }catch(IOException e){}
    System.out.println(
                      "ReceiverThread done, sum = " + sum);
  }//end run() method
  
  public void finalize(){
    System.out.println("Finalizing receiver thread");
  }//end finalize
  //-----------------------------------------------------//

}//end ReceiverThread class definition
//=======================================================//

class pipe01{ //controlling class
  public static void main(String[] args){
    System.runFinalizersOnExit(true);
    try{
      //Instantiate a PipedInputStream object
      PipedInputStream inStr = new PipedInputStream();
  
      //Instantiate a PipedOutputStream object and connect 
      // it to the existing PipedInputStream object
      PipedOutputStream pos = new PipedOutputStream(inStr);
  
      //Instantiate two thread objects
      SenderThread T1 = new SenderThread(pos );
      ReceiverThread T2 = new ReceiverThread(inStr );

      //And start the threads running  
      T1.start();
      T2.start();
    }catch(IOException e){}  
  }//end main()
  //-----------------------------------------------------//

  public void finalize(){
    System.out.println("Finalizing main thread");
  }//end finalize
  //-----------------------------------------------------//
}//end pipe01 class definition

Using Filtered Streams

We are going to take a look at filtered streams at this point because that information will be useful later.

The filtered stream classes make it possible for you to apply I/O filtering to data being processed by an object of an unfiltered stream class. For example, you could create a custom filter object to read a file and convert all the characters to upper case.

Filtered stream classes are subclasses of the abstract classes named FilterInputStream and FilterOutputStream.

There are several standard filtered streams available, and you can create your own. Those which are in the standard java.io package are:

The characteristics of each of these classes were discussed earlier.

To use a filtered stream class, you must associate an object of the filtered stream class with an object of the unfiltered stream class that will handle your I/O. You do this by instantiating an object of the filtered class by passing an object of the unfiltered class to the constructor for the filtered class.

You can instantiate the object of the unfiltered class beforehand, or you can simply call the constructor for the unfiltered class as a parameter to the constructor for the filtered class (create it as an anonymous object).

Having done this, you then have access to all the methods of the filtered class to use in your I/O efforts.

The following sample application is a rewrite of the previous application named files01.java. In the previous application, it was necessary to perform I/O one byte at a time. In this rewrite, The DataInputStream and DataOutputStream classes are associated with the FileInputStream and FileOutputStream classes. This makes it possible to use the methods of DataInputStream and DataOutputStream to control the I/O process.

This in turn makes it possible to perform the I/O one string at a time rather than one byte at a time. Numerous other methods are also available to perform the I/O in different ways.

Note that this program uses deprecated methods. The program named SampProg151 in the Review section at the end of the lesson shows how to replicate this program with no deprecated methods.
 

/* File files02.java Copyright 1997, R.G.Baldwin
This application is a modification of the application named
files01.  The purpose is to illustrate the use of filtered
I/O classes to write and then read a file, one string at a
time.

Objects of types DataOutputStream and DataInputStream are 
instantiated using objects of types FileOutputStream and
FileInputStream as parameters to the constructor.  This 
makes all of the methods for objects of types 
DataOutputStream and DataInputStream available to write 
and read the file.

The program was tested using JDK 1.1.3 under Win95.

The output from the program is:

Start the program and write a file
Get info about, read, and print the file
Path is C:\BALDWIN\Java\SampProg\Java\junk.txt
Length 13
Dick
Baldwin
End of program
**********************************************************/

import java.io.*;

class files02{
  public static void main(String[] args)
  {
    System.out.println(
                    "Start the program and write a file");
    try{

      //Instantiate and initialize a DataOutputStream 
      // object using a FileOutputStream object as a 
      // parameter to the constructor. This makes it 
      // possible to write to the file using the methods
      // of the DataOutputStream class.
      DataOutputStream dataOut = 
        new DataOutputStream(
                        new FileOutputStream("junk.txt"));
  
      //Write two strings to the file and close it
      dataOut.writeBytes("Dick\n");
      dataOut.writeBytes("Baldwin\n");
      dataOut.close();
    }catch(IOException e){}

    System.out.println(
              "Get info about, read, and print the file");
  
    //Instantiate an object of type file containing the 
    // name of the file.  Same as app named files01.java.
    File junkFile = new File("junk.txt");
  
    //Use the File object to get info about the file. 
    // Same as files01.java.
    System.out.println(
                 "Path is " + junkFile.getAbsolutePath());
    System.out.println("Length " + junkFile.length() );
  
    //Now read and print the data in the file
    try{
      //Instantiate a DataInputStream object on the 
      // FileInputStream object which uses the File object
      // named junkFile to open the stream and link to 
      // the file.
            
      //Note that the compiler reports readLine() as a
      // deprecated method and suggests using methods 
      // from the reader class instead.
      
      DataInputStream inData = 
        new DataInputStream(new FileInputStream(junkFile));

      String data; //temp holding area
  
      //Read and print strings until eof is indicated by 
      // null.  
      while( (data = inData.readLine()) != null)
        System.out.println(data);
      inData.close();
    }catch(IOException e){}
    System.out.println("End of program");
  }// end main
}//end class files02 definition

Reading and Writing Memory Buffers using Streams

The unfiltered classes ByteArrayInputStream and StringBufferInputStream make it possible for you to read data from memory buffer areas as though you were reading from an external device.

ByteArrayOutputStream can be used to write data into a memory buffer as though it were an external device.

ByteArrayInputStream and ByteArrayOutputStream will allow you to read and write 8-bit data. When you create these streams, you specify an existing byte array and then use the read() and write() methods to read from or write data to the array in memory.

StringBufferInputStream allows you to to read data from a String object. When you create a StringBufferInputStream object, you specify an existing String object as the source of the data and then use the read() methods to read from that object.

The following program illustrates the use of a StringBufferInputStream object to read the contents of a String object. Note that even though this class is named StringBufferInputStream, it is not designed to read the contents of objects of type StringBuffer.

Note that the following program uses deprecated code. The program named SampProg152.java in the Review section shows how to replicate this program without using any deprecated code.
 

/* File stream02.java
This application illustrates the use of a 
StringBufferInputStream object to read the contents of an
object of type String.

Note that even though this class is called 
StringBufferInputStream it does not read the contents of
objects of type StringBuffer.  Rather, the data source 
must be an object of type String.

The output from the program is:

Start the program and create a String object.
Create stream object of type StringBufferInputStream.
Read and display the contents of the String object.
This is an object of type String.
End of program
**********************************************************/
import java.io.*;
  
class stream02{//controlling class
  public static void main(String[] args)
  {
    System.out.println(
         "Start the program and create a String object.");
    String myString = "This is an object of type String.";
        
    System.out.println("Create stream object of type " +
                              "StringBufferInputStream.");
                              
    //Note that StringBufferInputStream is deprecated.  The
    // documentation recommends using the StringReader
    // class instead.
    StringBufferInputStream myStream = 
                    new StringBufferInputStream(myString);

    System.out.println("Read and display the contents " +
                                 "of the String object.");
          for(int cnt = 0; cnt < myString.length(); cnt++)
            System.out.print((char)myStream.read());
        
        System.out.println("\nEnd of program");
  }// end main
}//end class stream02 definition

Creating Your Own Filtered Stream Classes

The steps for creating your own filtered stream classes include the following:

The following application provides custom filtered input and output classes which make it possible to write objects of a specific type to a disk file and read them back.

A custom version of a write method is provided which decomposes the object into a series of bytes and writes those bytes to the file.

A custom version of a read method is provided which reads bytes from the file and reconstructs an object.

The user of the classes simply writes and reads objects and doesn't need to be concerned about the details of how the data is being handled.
 

/* File stream01.java Copyright 1997, R.G.Baldwin
This application illustrates the creation of custom filter
classes to filter I/O.  The classes can write objects of
type MyData to a disk file and read them back.

This application extends FilterOutputStream and 
FilterInputStream to implement the new filter classes.

The new filter output class named MyFilterOutputClass 
contains a method named MyWrite()

The MyWrite method accepts an input object of type MyData,
  converts it to a stream of bytes and writes them onto
  the disk using the write(int b) method of 
  FileOutputStream

The general contract for write is that one byte is written
  to the output stream. The byte to be written is the 
  eight low-order bits of the argument b which is an 
  integer. The 24 high-order bits of the integer b are 
  ignored.  

The individual bytes for the int data member named intData
  are selected and passed to the write() method by 
  shifting bits to the right 3 times, 8 bits per shift

This class does not override the write() method.  Rather,
  it provides a new MyWrite() method which makes use of 
  one form of the existing write() method.

The program was tested using JDK 1.1.3 under Win95.

The output from the program is:

Start the program and write an object to file
Read the object from the file and display it
The file contains X 123456789
End of program
**********************************************************/
import java.io.*;

class MyData{//data structure used for testing
  public char charData;
  public int intData;

  public MyData(){}//default constructor
  
  //parameterized constructor
  public MyData(char inChar, int inIntData){
    charData = inChar;
  intData = inIntData;
  }//end constructor
}//end class MyData definition
//=======================================================//

//This is a custom filter class for writing objects of
// type MyData into a file.
class MyFilterOutputClass extends FilterOutputStream{

  MyFilterOutputClass(FileOutputStream out) //constructor
  { super(out);} //pass the output object up the line
  
  //This is the new write method for objects
  void MyWrite(MyData obj){
    try{
      //write the character data member to the file
      write(obj.charData);
  
      //Now use bit shifting to decompose the integer 
      // data member into bytes and write them to the file
      for(int cnt = 0; cnt < 4; cnt++)
        write( (int) obj.intData  8*cnt);
    }catch(IOException e){}
  }//end write(MyData obj)
}//end MyFilterOutputClass definition
//=======================================================//

//This is a custom filter class for reading bytes from
// a file and constructing them into objects of type 
// MyData.
class MyFilterInputClass extends FilterInputStream{
  MyFilterInputClass(FileInputStream in) //constructor
  { super(in);} //pass the output object up the line

  //The following method reads bytes from a file and 
  // constructs an object of type MyData.  The object is
  // returned to the calling method.
  MyData MyRead(){
    MyData obj = new MyData();
    try{//read char data member from the file
      obj.charData = (char)read();
  
      //Now read bytes from the file and construct 
      // the integer data member
      for(int cnt = 0; cnt < 4; cnt++)
        obj.intData = obj.intData | 
                    ( (char)read()<< 8*cnt); //next 8 bits
    }catch(IOException e){}
    return obj; //return the constructed object
  }//end MyRead()
}//end MyFilterOutputClass definition
//=======================================================//
  
class stream01{//controlling class
  public static void main(String[] args){
    System.out.println(
          "Start the program and write an object to file");

    //Instantiate new type filter out obj linked to 
    // FileOutputStream
    try{
      MyFilterOutputClass fileOut = 
        new MyFilterOutputClass( 
                        new FileOutputStream("junk.txt") );
    
      //Construct an object of type MyData
      MyData myObject = new MyData('X',123456789);
    
      //Use new type filter method to write the object 
      // to the file
      fileOut.MyWrite(myObject);
      fileOut.close();
    }catch(IOException e){}

    System.out.println(
           "Read the object from the file and display it");
    //Instantiate new type filter inp obj linked to 
    // FileInputStream
    try{
      MyFilterInputClass fileIn = 
        new MyFilterInputClass( 
                         new FileInputStream("junk.txt") );
    
      //Use new filter method to read object from file
      MyData inData = fileIn.MyRead();
      fileIn.close();
  
      //Display the contents of the object
      System.out.println("The file contains " + 
                   inData.charData + " " + inData.intData);
    }catch(IOException e){}

    System.out.println("End of program");
  }// end main
}//end class stream01 definition

Reading and Writing Random Access Files

Using Random Access Files
Random Access Files and Filters
A Sample Program

The previous discussions have been concerned with sequential input/output. That is, input and output where the bytes are transferred as a sequential stream of bytes. This is a particularly appropriate form of I/O for communication with other computers via the network, etc.

However, when working with disk files, it is often desirable to be able to treat the data in the file as an an "array" of bytes and to be able to access those bytes anywhere within the array. This can be accomplished using the random access capabilities afforded by Java

Note that unlike other languages, the classes that we have been studying do not allow for appending data to the end of a file. In order to append data to the end of a file in Java, you must treat that file as a random access file.

Random access of file data in Java is supported by the RandomAccessFile class.
 

Using Random Access Files

The RandomAccessFile class can be used both for reading and writing. It implements the DataInput and DataOutput interfaces.

As with the sequential FileInputStream and FileOutputStream classes, you specify a file to be opened by name when you instantiate an object of class RandomAccessFile. You can specify the file name either by a literal string or by passing a File object which you have previously instantiated.

Unlike with the sequential classes, when you instantiate an object of RandomAccessFile, you must indicate whether you will just be reading the file, or also writing to it. You must be able to read a file before you can write to it.

The syntax for instantiating a RandomAccessFile object is as follows:
 

new RandomAccessFile(name, mode)

As mentioned earlier, the name can be specified using either a literal string or an object of type File. The mode is a literal string which can be either of two possibilities:

Once the file is opened you can use the various forms of the read___() and write___() methods to read and write the data in the file.

Simply being able to use the read and write methods to read or write the data in the file wouldn't be very interesting except for the availability of some additional methods. In particular, as in other languages which support random file I/O, RandomAccessFile supports the notion of a file pointer.

The file pointer points to the current location in the file. It points to the beginning of the file (value of 0) when the object is first instantiated. When you use the normal read and write methods, the value of the pointer is adjusted by the number of bytes actually read or written.

In addition, there are three methods available to explicitly manipulate the file pointer:

Random Access Files and Filters

As with sequential file I/O, we often want to be able to apply filtering to the data when we read and write a file on a random basis. Because RandomAccessFile doesn't inherit from the InputStream or OutputStream classes, you can't apply the same filters to objects of RandomAccessFile that you apply to the sequential streams.

However, because RandomAccessFile implements the DataInput and DataOutput interfaces, you can create a filter that works for either DataInput or DataOutput and it will work on those sequential access files that implement DataInput or DataOutput as well as any RandomAccessFile.

A Sample Program

The following sample program illustrates the use of the RandomAccessFile class to read and write bytes interior to a file.
 

/* File files03.java Copyright 1997, R.G.Baldwin
This application is a modification of the application 
named files02.  The purpose is to illustrate the use of 
the RandomFileAccess class to read and write data interior 
to the file.

Tested using JDK 1.1.3 under Win95

The output from the program is:

Start the program and write a file
Now open and read the file in random access mode
Display the entire file as characters.
Dick
Baldwin
Now display four bytes interior to the file.
Bald
Now write four bytes interior to the file.
Now display the entire file again.
Note that four bytes have been overwritten.
Dick
WXYZwin

End of program
**********************************************************/

import java.io.*;

class files03{
  public static void main(String[] args)
  {
    System.out.println(
                     "Start the program and write a file");
    try{

      //Instantiate and initialize a DataOutputStream 
      // object using a FileOutputStream object as a 
      // parameter to the constructor. This makes it 
      // possible to write to the file using the methods
      // of the DataOutputStream class.  This is a 
      // sequential stream operation and NOT a random 
      // access operation.
      DataOutputStream dataOut = new DataOutputStream(
                         new FileOutputStream("junk.txt"));
  
      //Write two strings to the file and close it
      dataOut.writeBytes("Dick\n");
      dataOut.writeBytes("Baldwin\n");
      dataOut.close();
    }catch(IOException e){}

    //Instantiate an object of type file containing the 
    // name of the file to illustrate the use of File 
    // objects in instantiating an object later of type 
    // RandomAccessFile
    File junkFile = new File("junk.txt");
  
    System.out.println(
       "Now open and read the file in random access mode");
    try{
      //Instantiate a RandomAccesSfile object for reading 
      // and writing using the File object named junkFile
      // to open and link to the file.
      RandomAccessFile inData = 
                       new RandomAccessFile(junkFile,"rw");
      int temp;

      System.out.println(
                 "Display the entire file as characters.");
      //Note that the file pointer is initially at the 
      // beginning of the file.
      while( (temp = inData.read()) != -1)
        System.out.print((char)temp);
  
      System.out.println(
           "Now display four bytes interior to the file.");
      //Get current location of the file pointer.
      long filePointer = inData.getFilePointer();
  
      //Set the file pointer to a location interior to 
      // the file.
      inData.seek(filePointer-8);
  
      //Now read and display four bytes.
      for(int cnt = 0; cnt < 4; cnt++)
        System.out.print( (char)inData.read() );
  
      System.out.println(
           "\nNow write four bytes interior to the file.");
      filePointer = inData.getFilePointer();
      inData.seek(filePointer-4);
      for(int cnt = 0; cnt < 4; cnt++)
        inData.write('W'+cnt);
  
      System.out.println(
                     "Now display the entire file again.");
      System.out.println(
            "Note that four bytes have been overwritten.");
      //Note that it is necessary to reposition the file 
      // pointer to the beginning of the file.
      inData.seek(0);
      while( (temp = inData.read()) != -1)
        System.out.print((char)temp);
  
      inData.close();
    }catch(IOException e){}
    System.out.println("\nEnd of program");
  }// end main
}//end class files03 definition

.

Reader and Writer Classes

The Reader and Writer classes were added to JDK 1.1 to support internationalization.  Briefly, the stream I/O capability that was supported prior to that release didn't support the use of 16-bit Unicode characters.  Rather, only the bottom 8 bits were handled by the streams.

The Reader and Writer classes make it possible to work with character streams rather than byte streams.  To a large extent, these character-stream classes mirror the byte stream classes, so if you know how to use one, it isn't too difficult to figure out how to use the other.  The web is full of discussions regarding the pros and cons of this situation, so I won't discuss it further at this point.  Rather, I am simply going to provide a sample program that will show you how to upgrade the previous program named files02.java to cause it to use character streams instead of byte streams.

To understand the differences, you should compare the code in this program with the code in files02.java.

Some of the code in the original program had nothing to do with the difference between byte streams and character streams, so that code was omitted from this version.

A listing of the program follows.  I have highlighted the new and important parts of this version in boldface to make it easier for you to compare this version with the original version.

/* File files04.java Copyright 1998, R.G.Baldwin
This program is an upgrade to the program named
files02.  The purpose is to convert the original program
that used byte streams for input and output to one that
uses unicode character streams for input and output.

To understand the differences, you should compare the
code in this program with the code in files02.java.

Some of the code in the original program had nothing to
do with the difference between byte streams and character
streams, so that code was omitted from this version.

The output from the program is:

Start the program and write a file
Read and print the file
Dick
Baldwin
End of program


The program was tested using JDK 1.1.6 under Win95.
**********************************************************/

import java.io.*;

class files04{
  public static void main(String[] args)
  {
    System.out.println(
                    "Start the program and write a file");
    try{
      //Open an output character stream using the Writer
      // classes.
      PrintWriter dataOut = 
         new PrintWriter(new FileWriter("junk.txt"),true);

      dataOut.println("Dick");
      dataOut.println("Baldwin");
  
      dataOut.close();

      System.out.println("Read and print the file");
      //Open an input character stream using the Reader
      // classes.
      BufferedReader inData = 
           new BufferedReader(new FileReader("junk.txt"));

      String data; //temp holding area
      while( (data = inData.readLine()) != null)
        System.out.println(data);
      inData.close();
    }catch(IOException e){}
    System.out.println("End of program");
  }// end main
}//end class files04 definition

Hopefully, this sample program will provide you with enough information to be able to convert your thinking and your programs from byte streams to character streams.

Review

Q - Usually the task of a stream is to move data from memory to an external device or vice versa, but streams can also be used to move data from one part of memory to another part of memory just as well: True or False. If false, explain why.

A - True.

Q - Write a Java application that illustrates writing and then reading a file one byte at a time. Instantiate your file stream objects using a literal string containing the name of the file. The output from the program should be similar to the following:
 

Start the program and write a file
Read, and display the file
Dick
End of program

A - See the program below:
 

/* File SampProg110.java from lesson 60
Copyright 1997, R.G. Baldwin
Without viewing the solution which follows, write a Java
application that illustrates writing and then reading a 
file one byte at a time.

=========================================================//
*/
import java.io.*;

class SampProg110{
  public static void main(String[] args){
    System.out.println(
    "Start the program and write a file");
    try{
      //Instantiate and initialize an output file stream 
      // object using a literal string containing the name
      // of the file.
      FileOutputStream outFile = new FileOutputStream(
        "junk.txt");
        
      //Write four bytes in the file and close it
      outFile.write('D');
      outFile.write('i');
      outFile.write('c');
      outFile.write('k');
      outFile.close();
    }catch(IOException e){}
    
    System.out.println("Read, and display the file");  
      
    //Now read and print the data in the file
    try{
      FileInputStream inFile = new FileInputStream(
        "junk.txt");
      int data;
        
      //Read and print until eof indicated by -1.  read() 
      // method returns integer.  Must cast as char to 
      // print. Otherwise, the numeric value of the byte 
      // is displayed.
      while( (data = inFile.read()) != -1)
        System.out.print((char)data);
      inFile.close();
    }catch(IOException e){}
    System.out.println();//new line
    System.out.println("End of program");
  }// end main
}//end class SampProg110 definition

Q - Write a Java application that illustrates writing and then reading a file one byte at a time. Store the uppercase characters A, B, C, D, and E in the file. Display the numeric unicode values of the characters. Instantiate your file stream object using an object of type File. Also use the object of type File to obtain and display two properties of the file: absolute path and length The output from the program should be similar to the following:
 

Start the program and write a file
Get info about, read, and print the file
Path is C:\BALDWIN\Cis2103K\Fall97\SampProg\junk.txt
Length 5
65 66 67 68 69 
End of program

A - See the program below.
 

/* File SampProg111.java from lesson 60
Copyright 1997, R.G.Baldwin
Without viewing the solution that follows, write a Java
application that illustrates writing and then reading a 
file one byte at a time.

=========================================================//
*/

import java.io.*;

class SampProg111{
  public static void main(String[] args)
  {
    System.out.println(
                     "Start the program and write a file");
    //Instantiate an object of type File
    File fileObj = new File("junk.txt");
        
    try{
      //Instantiate and initialize the stream object 
      // using the File object.
      FileOutputStream outFile = 
        new FileOutputStream(fileObj);
    
      //Write five bytes to the file and close it
      outFile.write('A');
      outFile.write('B');
      outFile.write('C');
      outFile.write('D');
      outFile.write('E');
      outFile.close();
    }catch(IOException e){}

    System.out.println(
      "Get info about, read, and print the file");  
    
    //Use the File object to get info about the file
    System.out.println("Path is " 
      + fileObj.getAbsolutePath());
    System.out.println("Length " + fileObj.length() );
  
    //Now read and print the data in the file
    try{
      //Instantiate and initialize the stream object 
      // using the File object.
      FileInputStream inFile = 
                              new FileInputStream(fileObj);
      int data;
  
      //Read and print until eof indicated by -1.  
      // read() method returns integer.  
      while( (data = inFile.read()) != -1)
        System.out.print(data + " ");
      inFile.close();
    }catch(IOException e){}
    System.out.println(); //new line
    System.out.println("End of program");
  }// end main
}//end class SampProg111 definition

Q - Write a Java application that illustrates the piping of the output from one thread to the input of another thread. The SenderThread sends the message "Dick Baldwin". The ReceiverThread receives and displays the message.

The output from your program should be similar to the following:
 

Starting ReceiverThread 
Starting SenderThread 
Received: Dick Baldwin 
ReceiverThread done 

A - See program below.
 

/* File SampProg112.java from lesson 60
Copyright 1997, R.G.Baldwin

Without viewing the following solution, write a Java
application that illustrates the piping of the output from 
one thread to the input of another thread.
=========================================================//
*/
import java.io.*;

class SenderThread extends Thread{
  PipedOutputStream pos; //ref to piped output stream obj
  String msgToSend = "Dick Baldwin";

  public SenderThread(PipedOutputStream posIn){
    pos = posIn; //save ref to piped output stream object
  }//end SenderThread constructor

  public void run(){ //override run method
    System.out.println("Starting SenderThread");
    try{
      for( int i = 0; i < msgToSend.length(); i++){
        //write a character to the piped output stream
        pos.write(msgToSend.charAt(i)); 
      }//end for loop
    }catch(IOException e){}
    //SenderThread has finished its task here
  }//end run() method
}//end SenderThread class definition

class ReceiverThread extends Thread{
  //ref to piped input stream obj
  PipedInputStream inputStr; 
  int inData = 0; //working variable
  
  public ReceiverThread(PipedInputStream inStr){
    inputStr = inStr; //save ref to piped input stream obj
  }//end ReceiverThread constructor

  public void run(){ //override run method
    System.out.println("Starting ReceiverThread");
    try{
      //read the first character as an integer
      inData = inputStr.read();
      System.out.print("Received: " + (char)inData);
      //read until integer -1 signals no more data
      while (inData != -1){ 
        //read next char as an integer
        inData = inputStr.read();
        System.out.print((char)inData);//display it
      }//end while loop
    }catch(IOException e){}
    System.out.println();//new line
    System.out.println("ReceiverThread done");
  }//end run() method
}//end ReceiverThread class definition


class SampProg112{ //controlling class
  public static void main(String[] args){
    try{
      //Instantiate a PipedInputStream object
      PipedInputStream inStr = new PipedInputStream();
  
      //Instantiate a PipedOutputStream object and connect 
      // it to the existing PipedInputStream object
      PipedOutputStream pos = new PipedOutputStream(inStr);
  
      //Instantiate two thread objects
      SenderThread T1 = new SenderThread(pos );
      ReceiverThread T2 = new ReceiverThread(inStr );

      //And start the threads running  
      T2.start();
      T1.start();
    }catch(IOException e){}  
  }//end main()
}//end SampProg112 class definition

Q - Write a Java application that writes a double, a long, and a character to a file and then reads the file and displays the data.

The output from the program should be similar to the following:
 

Start the program and write a file 
Read, and print the file 
Double: 3.14159 
Long: 1234 
Character: X 
End of program

A - See program below.
 

/* File SampProg113.java from lesson 60
Copyright 1997, R.G.Baldwin

Without viewing the solution that follows, write a Java
application that writes a double, a long, and a character
to a file and then reads the file and displays the data.
=========================================================//
*/

import java.io.*;

class SampProg113{
  public static void main(String[] args)
  {
    System.out.println(
      "Start the program and write a file");
    try{

      //Instantiate and initialize a DataOutputStream 
      // object using a FileOutputStream object as a 
      // parameter to the constructor. This makes it 
      // possible to write to the file using the
      // methods of the DataOutputStream class.
      DataOutputStream dataOut = 
        new DataOutputStream(
          new FileOutputStream("junk.txt"));

      //Write a double, a long, and a character to the file
      dataOut.writeDouble(3.14159);
      dataOut.writeLong(1234);
      dataOut.writeChar('X');
    }catch(IOException e){}

    System.out.println("Read, and print the file");  
  
    //Now read and print the data in the file
    try{
      //Instantiate a DataInputStream object on the 
      // FileInputStream object
      DataInputStream inData = 
        new DataInputStream(
          new FileInputStream("junk.txt"));
    
      //Read and print a double, a long, and a character
      System.out.println("Double: " + inData.readDouble());
      System.out.println("Long: " + inData.readLong());
      System.out.println("Character: " + inData.readChar());
      inData.close();
    }catch(IOException e){}
    System.out.println("End of program");
  }// end main
}//end class SampProg113 definition

Q - Write a Java application that writes a double, a long, and a character to an array of bytes in memory, and then reads the three data elements from the byte array and displays their values.

The output from the program should be similar to the following:
 

Write formatted data to a ByteArrayOutputStream object 
Read and display formatted data from the byte array 
Double: 3.14159 
Long: 1234 
Character: X 
End of program 

A - See the program below.
 

/* File SampProg114.java from lesson 60
Copyright 1997, R.G.Baldwin

This application illustrates the use of 
ByteArrayOutputStream and ByteArrayInputStream in 
conjunction with filtered I/O classes to write and then
read formatted data of specific mixed types into an array
of bytes in memory.

An object of type DataOutputStream is instantiated using
an object of type ByteArrayOutputStream as a parameter to
the constructor.  This makes all of the methods for objects
of type DataOutputStream available to write formatted data
into the memory represented by the ByteArrayOutputStream 
object.

A double, a long, and a character are written to the 
ByteArrayOutputStream object.

Then, an ordinary byte array is extracted from the
ByteArrayOutputStream object which contains the bytes
representing the double, the long, and the character.

This byte array is passed to a ByteArrayInputStream
constructor creating an object that is passed in turn to
a DataInputStream object.  This makes all of the methods
for objects of type DataInputStream available to read data
from the memory represented by the ByteArrayInputStream
object.

The double, long, and character data are read and 
displayed.

The output from the program should be similar to the 
following:
  
Write formatted data to a ByteArrayOutputStream object
Read and display formatted data from the byte array
Double: 3.14159
Long: 1234
Character: X
End of program
===========================================================
*/

import java.io.*;

class SampProg114{
  public static void main(String[] args)
  {
    System.out.println(
      "Write formatted data to a " 
        + "ByteArrayOutputStream object");
    //Instantiate the ByteArrayOutputStream object
    ByteArrayOutputStream myByteArrayOutputStream = 
      new ByteArrayOutputStream();
    try{
      //Instantiate and initialize a DataOutputStream 
      // object using the ByteArrayOutputStream object as a
      // parameter to the constructor. This makes it 
      // possible to write to the ByteArrayOutputStream 
      // object using the methods of the DataOutputStream 
      // class.
      DataOutputStream dataOut = 
        new DataOutputStream(myByteArrayOutputStream);

      //Write a double, a long, and a character to 
      // the ByteArrayOutputStream object  
      dataOut.writeDouble(3.14159);
      dataOut.writeLong(1234);
      dataOut.writeChar('X');
    }catch(IOException e){}
    
    //Extract an ordinary byte array from the 
    // ByteArrayOutputStream object to use below.
    byte[] myByteArray = 
      myByteArrayOutputStream.toByteArray();

    System.out.println(
     "Read and display formatted data from the byte array");
  
    try{
      //Instantiate a DataInputStream object on a 
      // ByteArrayInputStream object linked to the
      // ordinary byte array.  This makes it possible to
      // use the methods of the DataInputStream class to
      // read formatted data from the byte array.
      DataInputStream inData = 
        new DataInputStream(
          new ByteArrayInputStream(myByteArray));
    
      //Read and display a double, a long, and a character
      // from the byte array.
      System.out.println("Double: " + inData.readDouble());
      System.out.println("Long: " + inData.readLong());
      System.out.println("Character: " + inData.readChar());
      inData.close();
    }catch(IOException e){}
    
    System.out.println("End of program");
  }// end main
}//end class SampProg114 definition

Q - Write a Java application that instantiates an object of a user-defined type containing a single String data member, writes the object to a file as a stream of bytes, reads the stream of bytes from the file and reconstructs the object, and then displays the String data member.

The output from this program should be similar to the following:
 

Start the program and write an object to file 
Read the object from the file and display it 
The file contains First String 
End of program 

A - See program below.
 

/* File SampProg115.java from lesson 60

Copyright 1997, R.G.Baldwin

This application illustrates the creation of custom filter
classes to filter I/O.  The two custom filter classes can 
write objects of type MyData to a disk file and read them 
back.

This application extends FilterOutputStream and 
FilterInputStream to implement the new filter classes.

The new filter output class named MyFilterOutputClass 
contains a method named MyWrite()

The MyWrite method accepts an input object of type MyData,
converts it to a stream of bytes and writes them onto the
disk.

The output from this program should be similar to the
following:
  
Start the program and write an object to file
Read the object from the file and display it
The file contains First String
End of program  
===========================================================
*/
import java.io.*;

class MyData{//data structure used for testing
  public String firstString = "First String";
}//end class MyData definition
//=========================================================

//This is a custom filter class for writing objects of
// type MyData into a file.
class MyFilterOutputClass extends FilterOutputStream{
  MyFilterOutputClass(FileOutputStream out) //constructor
  { super(out);} //invoke superclass constructor
  
  //This is the new write method for objects
  void MyWrite(MyData obj){
    try{
      //write the string as a byte array     
      write(obj.firstString.getBytes());
    }catch(IOException e){}
  }//end write(MyData obj)
}//end MyFilterOutputClass definition
//=========================================================

//This is a custom filter class for reading bytes from
// a file and constructing them into an object 
// of type MyData.
class MyFilterInputClass extends FilterInputStream{
  MyFilterInputClass(FileInputStream in) //constructor
  { super(in);} //invoke superclass constructor

  //This is the new read method for objects. This method 
  // reads bytes from a file and constructs an object of 
  // type MyData.  The object is returned to the calling 
  // function.
  MyData MyRead(){
    MyData obj = new MyData();//construct empty object
    //Try to populate it by reading bytes and converting
    // them into a string.
    try{
      byte[] tempByteArray = new byte[40];
      in.read(tempByteArray);
      obj.firstString = new String(tempByteArray);
    }catch(IOException e){}
    return obj; //return the constructed object
  }//end MyRead()
}//end MyFilterOutputClass definition
//=========================================================
 
class SampProg115{//controlling class
  public static void main(String[] args){//main method
    System.out.println(
      "Start the program and write an object to file");
    
    //Instantiate new type filtered output obj linked to 
    // FileOutputStream
    try{
      MyFilterOutputClass fileOut = 
        new MyFilterOutputClass(
          new FileOutputStream("junk.txt") );
      
      //Construct an object of type MyData
      MyData myObject = new MyData();
      
      //Use new type filter method to write the object 
      // to the file
      fileOut.MyWrite(myObject);
      fileOut.close();
    }catch(IOException e){}

    System.out.println(
      "Read the object from the file and display it");
    //Instantiate new type filter input obj linked 
    // to FileInputStream
    try{
      MyFilterInputClass fileIn = 
        new MyFilterInputClass(
          new FileInputStream("junk.txt") );
      
      //Use new filter method to read object from file
      MyData inData = fileIn.MyRead();
      fileIn.close();
    
      //Display the contents of the object
      System.out.println("The file contains " 
        + inData.firstString);
    }catch(IOException e){}

    System.out.println("End of program");
    
  }// end main
}//end class SampProg115 definition

Q - Write a Java application that modifies four bytes interior to a file, displaying the contents of the file before and after the modification.

The output from the program should be similar to the following:
 

Start the program and write a file in sequential mode 
Now open and read the file in random access mode 
Display the entire file as characters. 
First String 
Second String 
Now display four bytes interior to the file. 
nd S 
Now write four bytes interior to the file. 
Now display the entire file again. 
Note that four bytes have been overwritten. 
First String 
SecoABCDtring

A - See the program below.
 

/* File SampProg116.java from lesson 60
Copyright 1997, R.G.Baldwin

First write a file containing two strings in sequential
mode and close the file.

Then open and display all the bytes in the file in random
access mode.

Then display four bytes interior to the file.

The modify the same four bytes interior to the file and 
display them again.

The output from the program should be similar to the
following:

Start the program and write a file in sequential mode
Now open and read the file in random access mode
Display the entire file as characters.
First String
Second String
Now display four bytes interior to the file.
nd S
Now write four bytes interior to the file.
Now display the entire file again.
Note that four bytes have been overwritten.
First String
SecoABCDtring

End of program
===========================================================
*/

import java.io.*;

class SampProg116{
  public static void main(String[] args)
  {
    System.out.println(
      "Start the program and write a file in " 
        + "sequential mode");
    try{
      //Instantiate and initialize a DataOutputStream 
      // object using a FileOutputStream object as a 
      // parameter to the constructor. 
      DataOutputStream dataOut = 
        new DataOutputStream(
          new FileOutputStream("junk.txt"));
    
      //Write two strings to the file and close it
      dataOut.writeBytes("First String\n");
      dataOut.writeBytes("Second String\n");
      dataOut.close();
    }catch(IOException e){}

    System.out.println(
      "Now open and read the file in random access mode");
    try{
      //Instantiate a RandomAccesSfile object for reading 
      // and writing and link it to the file that was
      // created above.
      RandomAccessFile inData = 
        new RandomAccessFile("junk.txt","rw");
      int temp;
  
      System.out.println(
        "Display the entire file as characters.");
      //Note that the file pointer is initially at the 
      // beginning of the file.
      while( (temp = inData.read()) != -1)
        System.out.print((char)temp);
    
      System.out.println(
        "Now display four bytes interior to the file.");
      //Get current location of the file pointer.
      long filePointer = inData.getFilePointer();
    
      //Set the file pointer to a location interior 
      // to the file.
      inData.seek(filePointer - 10);
    
      //Now read and display four bytes.
      for(int cnt = 0; cnt < 4; cnt++)
        System.out.print( (char)inData.read() );
    
      System.out.println(
        "\nNow write four bytes interior to the file.");
      filePointer = inData.getFilePointer();
      inData.seek(filePointer - 4);

      for(int cnt = 0; cnt < 4; cnt++)
        inData.write('A'+cnt);
    
      System.out.println(
        "Now display the entire file again.");
      System.out.println(
        "Note that four bytes have been overwritten.");
      //Note that it is necessary to reposition the 
      // file pointer to the beginning of the file.
      inData.seek(0);

      while( (temp = inData.read()) != -1)
        System.out.print((char)temp);
    
      inData.close();
    }catch(IOException e){}
    System.out.println("\nEnd of program");
  }// end main
}//end class SampProg116 definition

Q - Write a Java program that meets the following specifications.
 

/* File SampProg151.java Copyright 1997, R.G.Baldwin
From lesson 60.

Without viewing the solution that follows, write a Java
application that replicates the program named files02.java
but doesn't use deprecated methods.

This application is a modification of the application named
files01.  The purpose is to illustrate the use of filtered
I/O classes to write and then read a file, one string at a
time.

The program was tested using JDK 1.1.3 under Win95.

The output from the program is:

Start the program and write a file
Get info about, read, and print the file
Path is C:\BALDWIN\Java\SampProg\Java\junk.txt
Length 13
Dick
Baldwin
End of program
**********************************************************/

import java.io.*;

class SampProg151{
  public static void main(String[] args)
  {
    System.out.println(
                    "Start the program and write a file");
    try{

      //Instantiate and initialize a DataOutputStream 
      // object using a FileOutputStream object as a 
      // parameter to the constructor. This makes it 
      // possible to write to the file using the methods
      // of the DataOutputStream class.
      DataOutputStream dataOut = 
        new DataOutputStream(
                        new FileOutputStream("junk.txt"));
  
      //Write two strings to the file and close it
      dataOut.writeBytes("Dick\n");
      dataOut.writeBytes("Baldwin\n");
      dataOut.close();
    }catch(IOException e){}

    System.out.println(
              "Get info about, read, and print the file");
  
    //Instantiate an object of type file containing the 
    // name of the file.  Same as app named files01.java.
    File junkFile = new File("junk.txt");
  
    //Use the File object to get info about the file. 
    // Same as files01.java.
    System.out.println(
                 "Path is " + junkFile.getAbsolutePath());
    System.out.println("Length " + junkFile.length() );
  
    //Now read and print the data in the file
    try{
      //Instantiate a BufferedReader object on the 
      // FileReader object which uses the File object
      // named junkFile to open the stream and link to 
      // the file.
            
      /*The following note was extracted from the JDK 1.1.3
      documentation:
      Note: readLine() is deprecated. This method does not
      properly convert bytes to characters. As of 
      JDK 1.1, the preferred way to read lines of text is 
      via the BufferedReader.readLine() method. Programs 
      that use the DataInputStream class to read lines can
      be converted to use the BufferedReader class by 
      replacing code of the form
      
          DataInputStream d = new DataInputStream(in); 
      with 
          BufferedReader d
            = new BufferedReader(
              new InputStreamReader(in)); 
      */
      BufferedReader inData =
        new BufferedReader(new FileReader(junkFile));

      String data; //temp holding area
  
      //Read and print strings until eof is indicated by 
      // null.  
      while( (data = inData.readLine()) != null)
        System.out.println(data);
      inData.close();
    }catch(IOException e){}
    System.out.println("End of program");
  }// end main
}//end class SampProg151 definition

Q - Write a Java application that meets the following specifications.
 

/* File SampProg152.java Copyright 1998, R.G.Baldwin
From lesson 60

Without viewing the solution that follows, write a Java
application that replicates the program named Stream02.java
but that does not use deprecated methods.

This application illustrates the use of a StringReader 
object to read the contents of an object of type String.

The output from the program is:

Start the program and create a String object.
Create stream object of type StringReader.
Read and display the contents of the String object.
This is an object of type String.
End of program
**********************************************************/
import java.io.*;
  
class SampProg152{//controlling class
  public static void main(String[] args)
  {
    System.out.println(
         "Start the program and create a String object.");
    String myString = "This is an object of type String.";
        
    System.out.println("Create stream object of type " +
                              "StringReader.");
                              
    //Note that StringBufferInputStream is deprecated.  The
    // documentation recommends using the StringReader
    // class instead.
    StringReader myStream = 
                    new StringReader(myString);

    System.out.println("Read and display the contents " +
                                 "of the String object.");
    try{
            for(int cnt = 0; cnt < myString.length(); cnt++)
              System.out.print((char)myStream.read());
    }catch(IOException e){}
        
        System.out.println("\nEnd of program");
  }// end main
}//end class SampProg152 definition

Q - Write a Java program that meets the following specifications.
 

/*File SampProg155.java Copyright 1998, R.G.Baldwin
From lesson 60

Without viewing the solution that follows, write a Java
application that uses the new "reader" classes to read
lines of text from the keyboard and display the text
on the screen.  A line of text is terminated by pressing
the Enter key.

Continue reading and displaying lines until the user
enters "quit".

The program was tested using JDK 1.1.3 running under Win95.
**********************************************************/

import java.io.*;
//=======================================================//

public class SampProg155 {
  public static void main(String[] args){
    System.out.println(
           "Enter lines of text and they will be echoed");
    System.out.println("Enter the word quit to quit.");
    try{
      while(true){
        InputStreamReader is = 
                         new InputStreamReader(System.in);
        BufferedReader br = new BufferedReader(is);
        String s = br.readLine();
        if(s.compareTo("quit") == 0)break;
        System.out.println(s);
      }//end while
    }catch(IOException e){}
    System.out.println("Program terminated");
  }//end main
}//end class SampProg155
//=======================================================//

Q - Write a program that meets the following specifications.
 

/*File SampProg156.java
Copyright (c) Peter van der Linden,  May 5 1997.

Without viewing the solution that follows, write a Java
application that can read strings and all of the
primitive Java types from the keyboard.

This program was tested using JDK 1.1.3 under Win95.
I found it to experience intermittent problems when
used with JDK 1.1.3 under Win95.  I am assuming that
these intermittent problems will disappear when used
with a later version of the JDK. - rgb.

**********************************************************/
import java.util.*;
import java.io.*;
//=======================================================//

class SampProg156{
  public static void main(String[] args){
    EasyIn easy = new EasyIn();

    String myString;
    int myInt;
    
    System.out.println(
                "Enter a string");
    myString = easy.readString();
    System.out.println("Echo: " + myString);        
      
    System.out.println("Enter an int");
    myInt = easy.readInt();
    System.out.println("Echo: " + myInt);
    
    System.out.println("Terminating program");
        
  }//end main
}//end class SampProg156
//=======================================================//

// Simple input from the keyboard for all primitive types.
// Copyright (c) Peter van der Linden,  May 5 1997.
// Feel free to use this in your programs, as long as this
// comment stays intact.
//
// This is not thread safe, not high performance, and 
// doesn't tell EOF.
// It's intended for low-volume easy keyboard input.
// An example of use is:
//     EasyIn easy = new EasyIn();
//     int i = easy.readInt();   
// reads an int from System.in
// See Just Java and Beyond, Third Edition by Peter
// van der Linden


class EasyIn {    
  static InputStreamReader is = 
                       new InputStreamReader( System.in );
  static BufferedReader br = new BufferedReader( is );
  StringTokenizer st;

  StringTokenizer getToken() throws IOException {
    String s = br.readLine();
    return new StringTokenizer(s);
  }//end getToken()
    
  boolean readBoolean() {
    try {
      st = getToken();
      return new Boolean(st.nextToken()).booleanValue();
    }catch (IOException ioe) {
      System.err.println(
                    "IO Exception in EasyIn.readBoolean");
      return false;
    }//end catch
  }//end readBoolean()

  byte readByte(){
    try {
      st = getToken();
      return Byte.parseByte(st.nextToken());
    }catch (IOException ioe) {
      System.err.println(
                       "IO Exception in EasyIn.readByte");
      return 0;
    }//end catch
  }//end readByte()

  short readShort(){
    try {
      st = getToken();
      return Short.parseShort(st.nextToken());
    }catch (IOException ioe) {
      System.err.println(
                      "IO Exception in EasyIn.readShort");
      return 0;
    }//end catch
  }//end readShort()
    
  int readInt(){
    try {
      st = getToken();
      return Integer.parseInt(st.nextToken());
    }catch (IOException ioe) {
      System.err.println("IO Exception in EasyIn.readInt");
      return 0;
    }//end catch
  }//end readInt()

  long readLong(){
    try {
      st = getToken();
      return Long.parseLong(st.nextToken());
    }catch (IOException ioe) {
      System.err.println(
                      "IO Exception in EasyIn.readFloat");
      return 0L;
    }//end catch
  }//end readLong()

  float readFloat() {
    try {
      st = getToken();
      return new Float(st.nextToken()).floatValue();
    }catch (IOException ioe) {
      System.err.println(
                      "IO Exception in EasyIn.readFloat");
      return 0.0F;
    }//end catch
  }//end readFloat()

  double readDouble() {
    try {
      st = getToken();
      return new Double(st.nextToken()).doubleValue();
    }catch (IOException ioe) {
      System.err.println(
                     "IO Exception in EasyIn.readDouble");
      return 0.0;
    }//end catch
  }//end readDouble()

  char readChar() {
    try {
      String s = br.readLine();
      return s.charAt(0);
    }catch (IOException ioe) {
      System.err.println(
                       "IO Exception in EasyIn.readChar");
      return 0;
    }//end catch
  }//end readChar()

  String readString() {
    try {
      return br.readLine();
    }catch (IOException ioe) {
      System.err.println(
                     "IO Exception in EasyIn.readString");
      return "";
    }//end catch
  }//end readString
}//end class definition
//=======================================================//

-end-