Exception Handling in Java

                As we discussed in class, problems that arise in a Java program may generate exceptions or errors.   An exception is an object that defines an unusual or erroneous situation.  An exception is thrown by a program or the runtime environment and can be caught and handled appropriately if desired.  An error is similar to an exception except that an error generally represents an unrecoverable situation.

 

                A program can be designed to process an exception in one of three ways. It can

1.       Not handle the exception

2.       Handle the exception where it occurs

3.       Handle the exception at another point in the program

 

Up to the present, we have been doing case 1 above.  When these exceptions are not caught, these usually lead to an abnormal termination (ABEND) of the program, which we have all seen before in our programs.  Instead of depending on the system for exception handling, we can increase the program’s reliability and robustness if we catch the exception ourselves by including error recovery routines in our program.

                Some of the classes of IOExceptions we can have are:


 

 

 

 

 

 

 

 

 

 

 

 

 


ObjectStreamException

 
               

InvalidClassException

 
               

 

 

 

 

 

 

 

 

 

 



The class Exception and some fo tis subclasses are:

               

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 


Catching Exceptions

 

                Let us now examine how we catch and handle an exception when it is thrown.  The try statement identifies a block of statements that may throw an exception. A catch clause, which follows a try block, defines how a particular kind of exception is handled.  A try block can have several catch clauses associated with it.  Each catch clause is called an exception handler.  However, if there is more than one exception handler associated with a particular try block, the order in which they are put is important.

One cannot have an exception handler which is a special case of an exception handler before it.  This is because, when an exception is thrown, the program will try each of the exception handlers in the order in which they have been written. So if an exception handler subsumes one of its successors, that successor will never be able to handle an exception.  That is to say, once an exception handler has been executed, no other handlers can be executed for this particular thrown exception.

When a try statement is executed, the statements in the try block are executed in their normal execution. If no exception is thrown during the execution of the try block, processing continues with the statement following all of the catch clauses.  This situation is the normal execution flow and should occur most of the time. (It is as if the try…catch were not there.)

If an exception is thrown at any point during the execution of the try block, control is immediately transferred to the appropriate catch handler if it is present.  That is, control passes ot the first clause whose exception class corresponds to the class of the exception thrown.  After executing the statements in the catch clause, control transfers to the statement after the entire try statement.  If none of the exception handlers can catch the thrown exception, it is thrown to the operating system and the program usually experiences an abend.

 

If an exception is thrown and caught by the appropriate catch clause, then after the catch clause finishes executing, the next instruction to be executed is the first instruction after that clause.  That is to say, suppose one has:

 

 try

{

a;

}

catch(…)

{

c;
}

b;

 

Suppose during the execution of instruction a there is a run-time exception generated which is caught by the catch phrase.  After the execution of instruction c, then next instruction to be executed is instruction b.  Any instruction after a that is inside the try clause is not executed. Instruction a is not completed. 

 

 

IOExceptions

 

We typically try and catch IOExceptions when we open a file. In fact, Java insists that we throw an IOException every time we try and open a file. Even if we do not catch it. So for example, the following code would not compile in Java:

import java.io.*;

public void example()

{

                File input = new File(“example.txt”);     

 

}

The compiler would complain about IOException not being thrown.  We would at least need the following to compile correctly:

import java.io.*;

public void example() throws IOException

{

                File input = new File(“example.txt”);     

 

}

 

This is an example of the 3rd way to handle exceptions discussed in the first paragraph of these notes.  We can do this to handle the exception at this point (the 2nd example above) by the following code:

 

import java.io.*;

public void example()

{

                File input;

try

{             

                                 input = new File(“example.txt”);

}

catch(FileNotFoundException e)

{

                System.out.println(“Error in opening file “+e.getMessage()+” for file “+

                                                input.getAbsolutePath());

}

catch(IOException e)

{

                System.out.println(“IOException: “+e.getMessage());

}

}

 

 


What we are doing is first checking for if the file could not be found (FileNotFoundException). If it could not be found, we tell the user such (e.getMessage()) and tell the user where we are looking for the file (input.getAbsolutePath()).  If however, the problem is not that the file could not be found (e.g., it has protection properties which block us from opening it), the IOException is used to catch all other exceptions.

Note that the statement of declaration of input must be outside of the try block. This is because if we put the statement

 

File input = new File(“example.txt”0;

 

inside the try block, the statement of input.getAbsolutePath() would cause a compiler error. The reason for this is that the declaration of input only exists inside the block for which it is defined.  So the exception handlers will not have access to it.

 

                Similarly, if we tried to close the file input after the exception handlers, if we had the

File input = new File(“example.txt”);

inside the try block, the compiler would also generate an error.

 

Finally Clause

 

                A try statement can an optional finally clause.  The finally clause defines a section of code that is executed no matter how the try block is exited.  Most often, a finally clause is used to manage resources or to guarantee that particular parts of an algorithm are executed.  E.g, a file is closed.

                If no exception is generated, the statements in the finally clause are executed after the try block is complete.  If an exception is generated in the try block, control first transfers to the appropriate catch clause.  After executing the exception-handling code, control transfers to the finally clause and its statements are executed.  A finally clause, if present, must be listed following the last catch clause.

 

try
[

….

}

catch(….)

{

}

catch(….)

{

..

}

finally

{

 

}

 

 

 

Numerical Exceptions

                We can use exceptions to catch numerical problems in the program as well.   Frequently, one wants an input of a number, but a non-numerical input is given. When one tries to convert the input into a number (remember all input comes in as ASCII), the program terminates with an ABEND.  But there are ways to catch the bad data and proceed.

 

                For example, suppose one wants the user to input an integer.  To guarantee an integer is given, we can do this with the following code:

 

int age;

boolean keepGoing=true;

Scanner console;

 while(keepGoing)

{

                System.out.print(prompt);

                try

{

                age = console.nextInt();

                keepGoing = false;

}

catch(InputMismatchException e)

{

                System.out.println(“Invalid Entry. Please enter digits only”);

                console.next();//remove garbage left over in input buffer

}

}

 

The above code will keep running until the user inputs a legal integer.  If the user inputs bad data, e.g., 14a3, then the attempt to execute console.nextInt() will generate an InputMismatchException. It will be caught by the exception handler. However, the throwing of the exception causes the line of code of keepGoing = false; to be skipped over. The exception handler will then output an error message to the user. Because the exception was thrown by the bad input data of 14a3, the data is not removed from the input buffer.  So if it is not removed, we will have an infinite loop here as it will continually be read by the console.nextInt().  Thus, we need to remove it so the user can again input new data. That is the role of the console.next(). It will read the characters in the input buffer.  They are not used, but are thrown away.

 

                If, however, the user input good data, then console.nextInt() can convert the input into an integer to be stored in age.  Then the next line of code is executed: keepGoing = false;, and one can exit the while loop.

 

                We can combine arithmetic exceptions together just as we combined IOExceptions. For example, suppose we are input numbers and we want to divide the input. However, the user could give a zero for the denominator.  We can catch both exceptions by:

 

int dividend, divisor;

String inputString;

double quotient;

try

{

                inputString = JOptionPane.showInputDialog(“Give the numerator“);

                dividend = Integer.parseInt(inputString);

inputString = JOptionPane.showInputDialog(“Give the denominator“);

                divisor = Integer.parseInt(inputString);

                quotient = dividend/divisor;

}

catch(Arithemtic Exception e)

{

                JOptionPane.showMessageDialog(null,“Aritmetic  Exception: “+e.getMiessage());

}

catch(NumberFormatException e)

{

                JOptionPane.showMessageDialog(null,”NumberFormatException: “+inputString);

}

Exceptions with Arrays of Objects

 

                With arrays of objects, there are two common exceptions generated: (1) NullPointerException and (2) ArrayIndexOutOfBoundsException.  The first exception is generated when one allocates the array, but have not yet created an object for that particular index.  For example,

 

Fraction [ ] fract = new Fraction[10];

fract[0].getNumerator();

will generate a NullPointerException as fract[0] still has the value of Null (0).  Similarly, the code of fract[12] or fract[-1] will generate the ArrayIndexOutOfBoundsException.  Both of these exceptions can be caught in a try..catch block.  The order in which you put them (if you use them both in the same try clause) is immaterial as they are independent of each other.