I/O Framework

Introduction

Java views each file as a sequential stream of bytes. When a file is opened, an object is created and a stream is associated with the object. The streams provide communication channels between a program and a particular file or device. Object System.in (standard input stream object) enables a program to input bytes from the keyboard, object System.out (standard output stream object) enables a program to output data to the screen, and object System.err (standard error stream object) enables a program to output error messages to the screen. System.out can be redirected to send its output to a file. By convention, System.err stream is used to display error messages.

The java package java.io provides standard library classes to deal with streams. Classes in the package are divided into input and output streams, and then subdivided into those, which operate with characters data (text files) and with byte data (binary files).

The difference between a stream of characters from a stream of bytes is that the former uses Unicode characters, which are represented by 16 bits - that is it, in java, a character is made up of two bytes. In contrary, the C language uses 1 byte characters (ASCII table). The natural question to ask is how Java deals with these encoding problems: converting bytes to characters and back. The Java has different tables of encoding and you can choose any of them. To see the default table you print

System.getProperty("file.encoding");
One of the encoding tables is called UTF-8, which can represent ASCII characters (one byte), and other characters as two or three bytes. This is very important issue if you are concerned with writing applications that operate in an international context.

Standard Output

To print output values in our programs, we have been using System.out.print() and System.out.println(). The printf() method gives us more control over the output. Compare
System.out.println(Math.PI);
System.out.printf("%7.5f", Math.PI);
The first argument of printf() "%7.5f" is a format that describes how the second argument is to be printed. This format says, that we print a float number of 7 characters that includes 5 digits after the decimal point.

Command-line Input

Any Java program takes input values from the command line and prints output back to the terminal window. When you use the java command to invoke a Java program Demofrom the command line, you type something like this
java Demo parameter1 parameter2

Input values parameter1 and parameter2are passed to a main() method as an array of Strings.

Standard Input

The string of characters that you type in the terminal window after the command line is the standard input stream. The following code example (let us call this class Average.java)takes a parameter from the command line, then reads so many numbers from standard input and computes the average:
int n = Integer.parseInt(args[0]);
double sum = 0.0;
Scanner scanner = new Scanner(System.in); //it's defined in java.util

for(int k = 0; k < n; k++)
	sum += scanner.nextInt();

System.out.println("Average = " + sum/n);
You envoke this program by
java Average 4

Redirecting Standard Output

We can redirect a program's standard output to a file. The following command
java Average 4 > out.txt
redirect the output from the terminal window to a text file out.txt. Similarly, we can redirect standard input so that a program reads data from a file instead of the terminal application. For example,
java Average 4 < input.txt
reads four integers from a text file input.txt and prints their average to the terminal window. We can redirect the ouput to and from a file in one command line
java Average 4 < input.txt > out.txt

Exceptions

Exceptions in the object-oriented language is a quite complex topic that requires time to understand and apply properly. By now you perhaps met a few exception classes such as the following

NullPointerException

ArrayIndexOutOfBoundsException

ArithmeticException

NullPointerException

Here are three examples demonstrating what causes the NullPointerException:

String str = null;
int n = str.length();


String[] data = new String[10];
int k = data[5].length();


String s = null;
StringBuffer sb = new StringBuffer(s);

All exceptions are objects. Once an exception is thrown you have three options:

  1. do not handle it at all
  2. handle it where it occurs
  3. handle it later in the program

Consider this code segment in which a user is prompted to enter an integer:

Scanner scanner = new Scanner(System.in);
int m = scanner.nextInt();
If the user enters not integer, the above program will terminate abnormally - the nextInt() method will throw InputMismatchException.

That is your responsibility (as a programmer) to validate the input. One way to do this is to handle exceptions, meaning that you have to catch exceptions.

Catching Exceptions

To catch an exception, you have to use try and catch blocks. You wrap try around the statement (or a group of statements) which may throw an exception. Here is an example of how you would handle illegal input
Scanner scanner = new Scanner(System.in);
System.out.print("Enter an integer: ");
try
{
	int n = scanner.nextInt();
	System.out.println("You entered " + n);
}
catch (InputMismatchException e)
{
	System.out.println("The input is not an integer");
}
A catch block, which follows try, defines a particular exception you are trying to catch. Note, you may have several catch-blocks for one try-block: one catch- block for a particular exception. If an exception is thrown, control is immediately transferred to a correspondent catch-block, skipping all statements below the statement which generated an exception.

finally

The finally clause provides a mechanism for executing a section of code whether or not an exception is thrown or caught. Regardless to what happens in the try-block, the statements placed inside the finally-block will always execute. Those statements are usually like closing the input file, or database, or network connection.

Review the code example ExceptionDemo.java class.

Throwing Exceptions

It's common to test parameters of methods or constructors for valid input data. If the value is illegal, you throw an exception by using the throw keyword along with the object you want to throw. Examine this code example

public Card(int suit, int value)
{
   if (isValidValue(value) && isValidSuit(suit))
   {
      this.value = value;
      this.suit = suit;
   }
   else
      throw new RuntimeException("Illegal suit or value");
}

Throw behaves similar to return, it performs structured transfer from the place in your program where an abnormal condition was detected to a place where it can be handled. It also transmits information. The exception object may contain a detail message that can be retrieved by invoking the getMessage() method

Any method that may throw an exception, must declare the exception in it's method declaration. This is implemented by the use of the throws clause. Throws clause follows the method name and lists the exception types that may be thrown:

public static main(String[ ] args) throws IOException
{
}

The throws clause is used

When to handle exceptions

      Developer     Client  
 Method body     Throw the exception    Use a try/catch block 
 Method header    Declare the exception    Propagate the exception 

Java distinguishes checked and unchecked exceptions. A checked exception means that if you ignore it, your program will not compile. What do you do in this case? Either you use try-catch blocks or propagete the exception usingh throws clause.

Reading Text Files

There are two approaches of reading text files:

        		    I                         II

classes:		FileReader                  Scanner
          		BufferedReader              File


exceptions:		FileNotFoundException       FileNotFoundException
           		IOException                 IOException

methods:		readLine                    println
        		close                       close

The FileReader class represents an input file that contains character data. The class throws FileNotFoundException (checked exception), so you have to wrap try and catch block around each time you open a file. For the efficient reading, we use the BufferedReader class. It provides the method readLine() that allows to read the entire text line (a line is a sequence of characters terminated either by '\n' or '\r' or their combination). The method returns a String, or null if the end of the stream has been reached. The readLine() method throws IOException, so you have to handle it.

A text file can also be read using the Scanner class. Using the Scanner offers the advantage for text processing of various data formats.. Scanner has a lot of other features, with support for regular expressions, delimiter definitions, skipping input, searching in a line, reading from different inputs and others..

Review the code examples Read.java and ReadHTML.java

Writing to a Text File

        		writing

classes:		FileWriter
          		BufferdWriter
        		PrintWriter

exceptions:		IOException

methods:		println
        		close

The FileWriter class represents an output file that contains character data. It throws IOException. The class has minimal method support, like a method write , which writes a character or an array of characters or String. Unless prompt output is required, it is advisable to wrap a BufferedWriter around for efficiency reason. The class has the newLine() method, which uses the platform's own notion of line separator. Neither FileWriter nor BufferedWriter provides enough methods to deal with a formatted output. For such purposes we shall use another class PrintWriter on the top of the previous two classes. The class uses familiar methods print and println.

Review the code example Write.java

New Line

The line separator string depends on your operating system. On UNIX, the value of the line separator is "\n", on Windows - "\r\n", and on Mac - "\r" . In Java the line separator is defined by the system property line.separator , which should be called as it follows

String out = "any characters";
out += System.getProperty("line.separator");

Appending Data

The class FileWriter has a second argument, which is boolean, indicating whether or not to append the data written. Here is a code fragment,
BufferedWriter out = new BufferedWriter (new FileWriter("out.txt", true));

Serialization

Object is serialized by transforming it into a sequence of bytes. Once serialized, the object (actually its data fields) can be stored in a file to be read later, or sent across a network to another computer. Serialization allows you to create persistent objects, objects that can be stored and then reconstituted for later use. The basic mechanism of serialization is simple. To serialize an object, you create an output stream
FileOutputStream out = new FileOutputStream("output.ser");
ObjectOutputStream outStream = new ObjectOutputStream(out);

and then use a method writeObject() to write an object into a file in a binary format. It throws IOException and NotSerializableException.

outStream.writeObject("Today");
outStream.writeObject(new Date());
outStream.flush();

You can write primitive data types as well. They are written to the stream with the methods, such as writeInt, writeFloat, and a few others.

The class of each serializable object is encoded including the class name and signature of the class, the values of the object's fields and arrays, and the closure of any other objects referenced from the initial objects. Only objects with the Serializable interface can be serialized. The interface does not have any methods, and serves only as a flag to the compiler. For most classes the default serialization is sufficient. Many classes of API implement Serializable interface, for example String, Vector.

Reading an object from a stream, like writing, is straightforward. To reverse the process, we first create an input stream,

FileInputStream in = new FileInputStream ("output.ser");
ObjectInputStream inStream = new ObjectInputStream(inStream);
and then invoke readObject() method to read the information from "output.dat" file.
String today = (String) inStream.readObject();
Date date = (Date) inStream.readObject();
readObject() throws ClassNotFoundException and IOException checked exceptions. Primitive data types are read from the stream with the methods, such as readInt, readFloat.

The default serialization process serializes each field of the object except static and transient. Using transient, we can exclude particular information when we serialize the oblect. An example of such excluded information could be a password.

Review the code example SerializationDemo.java