Java – I/O and Exceptions

Input, Output, and Exception Handling

 

File Output

We have seen how to send information to System.out so that it can be seen by the user. But suppose that we want to keep a permanent record in a file. We will need to give the file a name so that the operating system knows where to save our information, and we need to connect our program to the file.

Before we do this however, we need to make a decision. Is the information going to be read by a person or are we just saving it so that another program can read it back in? Java provides different classes for these purposes. In Java, Writers are used to store information in the form of readable characters and OutputStreams are used when the data needn’t be read by people. Information written by a Writer can later be read by a Reader. Information written by an OutputStream will usually later be read by an InputStream.

A FileWriter, defined in the java.io package, will connect you to an external file.


FileWriter writer = new FileWriter("data.txt");

We can now write characters to the file named writer. There are two things to note about this, however. First is that the operating system may not be able to provide the file for us. If this happens we have an error and the Java system will “throw” an exception. More specifically, it will throw an IOException, also defined in java.io. Exceptions, throwing, and catching are explained in the next Section. In general, errors can occur with most forms of input and output.

The second thing to note is that a FileWriter isn’t very convenient. We can write characters to it, but not much else. The integer 68 is internally encoded not as two characters, but as an int. If we try to write 68 to a FileWriter (writer.write(68);) something unexpected will occur. The character “D” will be written since 68 is the internal character code for a “D”.

In Java, the input/output classes are each designed to do one job well and several classes are used together to do sophisticated things. The way in which the Java I/O classes work follows a general pattern called Decorator. Many of the I/O classes are used to decorate objects of another I/O class by providing some “fancy packaging.” We looked at decorators for strategies in Chapter 4. Here we have a different kind of decorator, but the same idea exactly. A decorator for a Writer will itself be a writer and will provide some additional services that the object it decorates does not provide. Here, we would like to use a PrintWriter, which knows how to write most of the things known to a Java program. A print writer doesn’t actually print, however. It just knows how to format the things you give it (like ints) and pass them to the writer it decorates. We must layer up a PrintWriter onto a FileWriter to get output to a file. This is quite easily done also, though it too may throw an IOException.


PrintWriter output = new PrintWriter(new FileWriter("data.txt"));

Notice that we have created two objects here. First we create a FileWriter for the file “data.txt”, but we don’t give that object a name. Instead we pass this as a parameter to the PrintWriter constructor. Therefore the PrintWriter will decorate the FileWriter. We can pass ints to the PrintWriter, which will translate them into characters and then pass them to the FileWriter, which will itself put them into the file.


output.print(68);

A PritntWriter can print ints, doubles, Strings, etc. It can also println any of these things, in which case an end of line mark will be added after the value is printed. The above will output the character 6 followed by the character 8 and these will appear in the file data.txt.

One important thing to note is that when you open a Writer or an OutputStream in a program, you need to close it before the program exits if you want the contents of the stream to be preserved.


output.close();

If you forget this here, you won’t find the file when the program exits.

A PrintWriter can decorate either another Writer or an OutputStream. This means that you can write ints and doubles, for example, to data files as well as character files though the data will be stored as characters if you do this.

Another output decorator is BuffferedWriter. This class might be used if you only wanted to write Strings to a Writer, such as a FileWriter. It provides internal buffering that makes the output more efficient by holding on to the information we write until there is quite a bit of it and then writing it all at once. Since the physical write process to a file takes a long time, this can speed up a program. The last output decorator we will discuss here is the OutputStreamWriter which decorates an OutputStream, turning it into a Writer.

Sometimes you need to layer more than one decorator onto another object. Here we seldom use more than two decorators and one object that is not a decorator. The outermost decorator is the one you wish to actually use. The innermost Writer gives the final destination for the characters or other data you produce.

 Exceptions

Errors occur in Java programs as in most human activity. When an error occurs in a running Java program we say than an exception (small e) has occurred. When this happens an Exception object (capital e) is created and “thrown”. This can be caught or not, depending on the kind of Exception it is and on the program.

It is easy to generate an exception. Simply declare a new Robot variable and send it a message without actually creating any robot.


Robot John;
John.move();

This will generate a NullPointerException which will be thrown, but not caught. The program will terminate and we will get a message informing us of the fact, perhaps even telling us which line in our program contained the error. (Actually, it is just the thread that executes the error that terminates, not necessarily the entire program.)

We can catch this exception and therefore prevent the program from terminating. To catch an exception if it arises you include the statements that might throw the exception in a try block.


try
{	Robot John;
	John.move();
}
catch(NullPointerException e)
{	System.out.println(" A robot was not initialized: "  + e);
}

When we catch an exception we name the class of the exception we are interested in and give a variable that will have that type. Within the body of the catch clause we can send the Exception object messages, or print it out as here.

When an exception is thrown in a try block, the execution of the try is abandoned immediately and the system searches for a handler (a catch clause) of the type of the exception that was thrown.

If the code can throw more than one kind of exception, then you can have several catch clauses with a single try. The most specific ones should be listed first, and the more general ones later. Exceptions are objects from classes. Here, more general means a super class, and more specific means a sub class. This is because Exceptions behave polymorphically.

There are two basic kinds of exceptions in a Java program: Exception and RuntimeException. A null pointer exception is of the latter kind and these need not be caught. In fact, a correct program should never have any of these, so if you have one, you need to fix your program. In the above we need to make John refer to some robot before we send it any messages.

The other kind, Exception, is one that can occur even in a correct program. For example, when we try to create a file, the operating system may not permit it, perhaps because the disk is locked, or there is no space available. These kinds of problems are beyond the control of the programmer. Therefore, even a correct program can generate them. For this reason such exceptions must be caught by the programmer. The IOExceptions discussed in Section 10.1 are like this. So they must be caught.


PrintWriter output = null;
try
{ 	output = new PrintWriter(new FileWriter("data.txt"));
	...
}
catch(IOException e)
{	System.out.println(" File could not be created: "  + e);
	System.exit(1);
}

Here we have indicated with ellipsis that some things have been left out. The question arises as to how big a try block should be. This depends on whether you can recover from the exception or not. If you can’t create a file, then you can’t write to it, and perhaps your program can’t continue. In this case, any statement that writes to the file should also be in the same try block. If it were outside, then we might be trying to write to a file that we haven’t created. Here the variable output would still be null if the exception is thrown.

Another possibility, not applicable here, is to provide some default value for the information that was being created when the exception was thrown. If this can be reasonably done, then the program can continue, and the try block can be relatively short. This might be possible if ArithmeticException is thrown because we divide by zero.

In some circumstances, an exception must be caught, but it doesn’t make sense to catch it at the point at which it was thrown. In this case we need to propagate it. For example, suppose we want to create a method to open an output file for us. It might make sense for the caller of this function to handle any exception thrown when it executes.


public PrintWriter openFile(String filename) throws IOException
{	return new PrintWriter(new FileWriter( s ));
}

Then, calls to this function should be enclosed in try blocks.

 Input

Input is the opposite of output. We are trying to obtain information from somewhere for the program. The principles of use via decorators are the same as for output, but more exceptions may be thrown. In face, nearly every input statement might throw an exception since the source of the data might have become unavailable.

We can read from a file using a FileReader or FileInputStream, the former if the file contains character data. We can layer on a BufferedReader to read Strings from the file using the readLine method of BufferedReader. This will read an entire line of input, where it is assumed that the file is broken up into lines, perhaps because it was written using the println methods of a PrintWriter.

So far, so good, but what if we want to read other things besides Strings. As it turns out, there is no exact input analogue of a PrintWriter. Instead we use a Bufferedreader and its readLine method, and a special object called a StringTokenizer from the java.util package.

A StringTokenizer is an object that can break up a String into pieces separated by characters of our choice. To create a StringTokenizer initialized with information from a file, we might do something like the following.


try
{ 	BufferedReader input = new BufferedReader (new FileReader("data.txt"));	
	String str
	while((str = input.readLine()) != null)
	{	StringTokenizer tokens = new StringTokenizer(str);
		...
	}
}
catch(IOException e)
{	System.out.println(" File could not be opened: "  + e);
	System.exit(1);
}

Note that a readLine will return null at the end of the file. We use this fact to exit the loop. Notice that the while test contains an assignment to str as well as a test of the result. This seems like very strange practice, but it is a standard Java idiom, so you need to become familiar with it.

If we are inside the while loop, we have a non empty string, so we wrap a StringTokenizer around it to give us access to its parts.

Once we have the tokenizer we operate on it to get access to its string. We do this by writing a while loop controlled by calls to the tokenizer’s hasMoreTokens method. We get access to an individual “word” or “token” in the string with the tokenizer’s nextToken method. Continuing the above, and repeating part of it:

 

while((str = input.readLine()) != null)
{	StringTokenizer tokens = new StringTokenizer(str);
	while (tokens.hasMoreTokens())
	{ 	String word = tokens.nextToken();
		...
	}
}

Now, we can do anything we like with the String named Word. If we know that it encodes an int, for example, we can get that int with


	int ivalue = Integer.parseInt(word);

though this latter message may throw a NumberFormatException if the String doesn’t actually contain an int value. Integer is a class in java.lang that is usually used to turn ints into objects, though it also provides this static method parseInt to translate Strings.

Notice that to do the above effectively, we need to know what to anticipate at each “word” in the file.

There is another variation on the StringTokenizer constructor in which we give it a string as the second parameter. The individual characters in this string will be taken as the ones that are used to break up the String. For example, we could have a String of works separated with slashes and tokenize this with a tokenizer whose second parameter was just “/”. If we don’t give this parameter, the default value is used with consists of the so-called white space characters: space, tab, and newline.

 File At A Time Input

In some ways the most efficient way to read a file, assuming that it isn’t absolutely HUGE, is to read it all at once into a buffer. The FileInputStream class lets us ask how big a file is with its available method. So we base this on that class, but we layer an InputStreamReader on top of it. This class will let us read a buffer (char array) of any size, so we create one of the size of the file. We then read the entire file into the buffer and then put the characters in the buffer into a new String. This we can wrap with a StringTokenizer.


	try
	{	FileInputStream f = new FileInputStream("republicOfPlato.txt");
		InputStreamReader read = new InputStreamReader(f);
		int bufsize = f.available();
		char [] text = new char [bufsize];
		read.read(text, 0, bufsize); // You have the entire file in this array. 
		System.out.print(text); // Show the file. 
			
		String s = new String(text); // Now the array is in a string
		StringTokenizer t = new StringTokenizer(s, "\r\n"); 
			// break up on return and newline characters. 
		while(t.hasMoreTokens())
			System.out.println(t.nextToken()); // show the file one line per line. 
	}
	catch(IOException e){System.out.println("Failed");}

Here we set up the tokenizer to break into lines by using the newline and return characters as token separators: “\r\n”

 Character At A Time Input

Sometimes you really do need to process a file one character at a time. There are a couple of standard tricks for doing this. Here we will use a buffered reader for efficiency only, not to get access to its readLine method.


	try // reading a byte at a time
	{	BufferedReader b = new BufferedReader(new FileReader("republicOfPlato.txt "));
		int ch; // YES int;
		while((ch = b.read()) >= 0) // YES assignment. Note: the parens are required.
		{	char c = (char)ch; // cast to char
			... // Do whatever you like with c. 
		}
	}
	catch(IOException e){System.out.println("Failed");}

When we read the file b, we read an int, not a char. This is because an int (32 bits) is bigger than a char (16 bits) so the system can signal end of file with a negative value. We do the read in the control part of a while loop in which we again have an assignment to the int variable and a test for non negative.

Exception Hierarchy:

All exception classes are subtypes of the java.lang.Exception class. The exception class is a subclass of the Throwable class. Other than the exception class there is another subclass called Error which is derived from the Throwable class.

Errors are not normally trapped form the Java programs. These conditions normally happen in case of severe failures, which are not handled by the java programs. Errors are generated to indicate errors generated by the runtime environment. Example : JVM is out of Memory. Normally programs cannot recover from errors.

The Exception class has two main subclasses: IOException class and RuntimeException Class.

Java ExceptionsHere is a list of most common checked and unchecked Java’s Built-in Exceptions.

Exceptions Methods:

Following is the list of important medthods available in the Throwable class.

SN Methods with Description
1 public String getMessage()
Returns a detailed message about the exception that has occurred. This message is initialized in the Throwable constructor.
2 public Throwable getCause()
Returns the cause of the exception as represented by a Throwable object.
3 public String toString()
Returns the name of the class concatenated with the result of getMessage()
4 public void printStackTrace()
Prints the result of toString() along with the stack trace to System.err, the error output stream.
5 public StackTraceElement [] getStackTrace()
Returns an array containing each element on the stack trace. The element at index 0 represents the top of the call stack, and the last element in the array represents the method at the bottom of the call stack.
6 public Throwable fillInStackTrace()
Fills the stack trace of this Throwable object with the current stack trace, adding to any previous information in the stack trace.

Catching Exceptions:

A method catches an exception using a combination of the try and catch keywords. A try/catch block is placed around the code that might generate an exception. Code within a try/catch block is referred to as protected code, and the syntax for using try/catch looks like the following:

try
{
   //Protected code
}catch(ExceptionName e1)
{
   //Catch block
}

A catch statement involves declaring the type of exception you are trying to catch. If an exception occurs in protected code, the catch block (or blocks) that follows the try is checked. If the type of exception that occurred is listed in a catch block, the exception is passed to the catch block much as an argument is passed into a method parameter.

Example:

The following is an array is declared with 2 elements. Then the code tries to access the 3rd element of the array which throws an exception.

// File Name : ExcepTest.java
import java.io.*;
public class ExcepTest{

   public static void main(String args[]){
      try{
         int a[] = new int[2];
         System.out.println("Access element three :" + a[3]);
      }catch(ArrayIndexOutOfBoundsException e){
         System.out.println("Exception thrown  :" + e);
      }
      System.out.println("Out of the block");
   }
}

This would produce the following result:

Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
Out of the block

Multiple catch Blocks:

A try block can be followed by multiple catch blocks. The syntax for multiple catch blocks looks like the following:

try
{
   //Protected code
}catch(ExceptionType1 e1)
{
   //Catch block
}catch(ExceptionType2 e2)
{
   //Catch block
}catch(ExceptionType3 e3)
{
   //Catch block
}

The previous statements demonstrate three catch blocks, but you can have any number of them after a single try. If an exception occurs in the protected code, the exception is thrown to the first catch block in the list. If the data type of the exception thrown matches ExceptionType1, it gets caught there. If not, the exception passes down to the second catch statement. This continues until the exception either is caught or falls through all catches, in which case the current method stops execution and the exception is thrown down to the previous method on the call stack.

Example:

Here is code segment showing how to use multiple try/catch statements.

try
{
   file = new FileInputStream(fileName);
   x = (byte) file.read();
}catch(IOException i)
{
   i.printStackTrace();
   return -1;
}catch(FileNotFoundException f) //Not valid!
{
   f.printStackTrace();
   return -1;
}

The throws/throw Keywords:

If a method does not handle a checked exception, the method must declare it using the throws keyword. The throws keyword appears at the end of a method’s signature.

You can throw an exception, either a newly instantiated one or an exception that you just caught, by using the throw keyword. Try to understand the different in throws and throw keywords.

The following method declares that it throws a RemoteException:

import java.io.*;
public class className
{
   public void deposit(double amount) throws RemoteException
   {
      // Method implementation
      throw new RemoteException();
   }
   //Remainder of class definition
}

A method can declare that it throws more than one exception, in which case the exceptions are declared in a list separated by commas. For example, the following method declares that it throws a RemoteException and an InsufficientFundsException:

import java.io.*;
public class className
{
   public void withdraw(double amount) throws RemoteException,
                              InsufficientFundsException
   {
       // Method implementation
   }
   //Remainder of class definition
}

The finally Keyword

The finally keyword is used to create a block of code that follows a try block. A finally block of code always executes, whether or not an exception has occurred.

Using a finally block allows you to run any cleanup-type statements that you want to execute, no matter what happens in the protected code.

A finally block appears at the end of the catch blocks and has the following syntax:

try
{
   //Protected code
}catch(ExceptionType1 e1)
{
   //Catch block
}catch(ExceptionType2 e2)
{
   //Catch block
}catch(ExceptionType3 e3)
{
   //Catch block
}finally
{
   //The finally block always executes.
}

Example:

public class ExcepTest{

   public static void main(String args[]){
      int a[] = new int[2];
      try{
         System.out.println("Access element three :" + a[3]);
      }catch(ArrayIndexOutOfBoundsException e){
         System.out.println("Exception thrown  :" + e);
      }
      finally{
         a[0] = 6;
         System.out.println("First element value: " +a[0]);
         System.out.println("The finally statement is executed");
      }
   }
}

This would produce the following result:

Exception thrown  :java.lang.ArrayIndexOutOfBoundsException: 3
First element value: 6
The finally statement is executed

Note the following:

  • A catch clause cannot exist without a try statement.
  • It is not compulsory to have finally clauses when ever a try/catch block is present.
  • The try block cannot be present without either catch clause or finally clause.
  • Any code cannot be present in between the try, catch, finally blocks.

Declaring you own Exception:

You can create your own exceptions in Java. Keep the following points in mind when writing your own exception classes:

  • All exceptions must be a child of Throwable.
  • If you want to write a checked exception that is automatically enforced by the Handle or Declare Rule, you need to extend the Exception class.
  • If you want to write a runtime exception, you need to extend the RuntimeException class.

We can define our own Exception class as below:

class MyException extends Exception{
}

You just need to extend the Exception class to create your own Exception class. These are considered to be checked exceptions. The following InsufficientFundsException class is a user-defined exception that extends the Exception class, making it a checked exception. An exception class is like any other class, containing useful fields and methods.

Example:

// File Name InsufficientFundsException.java
import java.io.*;

public class InsufficientFundsException extends Exception
{
   private double amount;
   public InsufficientFundsException(double amount)
   {
      this.amount = amount;
   } 
   public double getAmount()
   {
      return amount;
   }
}

To demonstrate using our user-defined exception, the following CheckingAccount class contains a withdraw() method that throws an InsufficientFundsException.

// File Name CheckingAccount.java
import java.io.*;

public class CheckingAccount
{
   private double balance;
   private int number;
   public CheckingAccount(int number)
   {
      this.number = number;
   }
   public void deposit(double amount)
   {
      balance += amount;
   }
   public void withdraw(double amount) throws
                              InsufficientFundsException
   {
      if(amount <= balance)
      {
         balance -= amount;
      }
      else
      {
         double needs = amount - balance;
         throw new InsufficientFundsException(needs);
      }
   }
   public double getBalance()
   {
      return balance;
   }
   public int getNumber()
   {
      return number;
   }
}

The following BankDemo program demonstrates invoking the deposit() and withdraw() methods of CheckingAccount.

// File Name BankDemo.java
public class BankDemo
{
   public static void main(String [] args)
   {
      CheckingAccount c = new CheckingAccount(101);
      System.out.println("Depositing $500...");
      c.deposit(500.00);
      try
      {
         System.out.println("\nWithdrawing $100...");
         c.withdraw(100.00);
         System.out.println("\nWithdrawing $600...");
         c.withdraw(600.00);
      }catch(InsufficientFundsException e)
      {
         System.out.println("Sorry, but you are short $"
                                  + e.getAmount());
         e.printStackTrace();
      }
    }
}

Compile all the above three files and run BankDemo, this would produce the following result:

Depositing $500...

Withdrawing $100...

Withdrawing $600...
Sorry, but you are short $200.0
InsufficientFundsException
        at CheckingAccount.withdraw(CheckingAccount.java:25)
        at BankDemo.main(BankDemo.java:13)

Common Exceptions:

In Java, it is possible to define two catergories of Exceptions and Errors.

  • JVM Exceptions: – These are exceptions/errors that are exclusively or logically thrown by the JVM. Examples : NullPointerException, ArrayIndexOutOfBoundsException, ClassCastException,
  • Programmatic exceptions: – These exceptions are thrown explicitly by the application or the API programmers Examples: IllegalArgumentException, IllegalStateException.

Leave a comment