CS 596 Java Programming Fall Semester, 1998 Exceptions |
||
---|---|---|
© 1998, All Rights Reserved, SDSU & Roger Whitney San Diego State University -- This page last updated 21-Oct-98 |
Exceptions
Exception Handling
In a program there may be events considered exceptional or unusual
Boolean computeFoobar( ) { Boolean zeroDivideFlag = false; if ( A * A - 2 * B = 0 ) zeroDivideFlag = true; else result = sqrt( C )/ ( A * A - 2 * B ) return zeroDivideFlag } // in main flag = computeFoobar( ) if ( flag == true ) // now what?
Definitions Exception
Defining Exceptions
In Java all exceptions are instances of java.lang.Exception or one of its subclasses. To create a new type of exception you create a subclass of java.lang.Exception. Like any class, you can add any fields or methods you like. Normally any fields or methods added to an exception are used pass information to the exception handler about the exception. Exceptions have two constructors: one with no parameters, one with a string parameter. The string parameter is used to pass information to the exception handler. Follow the standard and make sure that any exceptions you create have proper constructors.
class BoringLectureException extends Exception { public BoringLectureException() { super(); } public BoringLectureException( String errorMessage ) { super( errorMessage ); } } class BoringInstructor extends Exception { public String name; public String course; public BoringInstructor () { super(); } public BoringInstructor ( String errorMessage ) { super( errorMessage ); } }
How is the exception raised?
All exceptions started or raised by a throws statement. However, there are times when you do not see the throws statement. The throws may be in code you are using (the Java API for example). The throws may be in the JVM, which is sometimes called an implicitly raised exception, as is shown below.
Implicitly by some error condition class ImplicitlyRaisedException { public static void main( String[] arguments ) { int students[] = new int[5]; students[ 10 ] = 1; // Exception occurs here } }
Explicitly by the Programmer
class ExplicitlyRaisedException { public static void main( String[] arguments ) { throw new ArrayIndexOutOfBoundsException(); } }
Handling an Exception - Use Try
Raising an exception overrides the normal flow of control of the program. The flow of control is transferred to the "enclosing" try-catch block. The JVM wraps programs with a global try-catch block, which handles any exception not handled in the program. The global try-catch block prints out a message and exits the program. We will see later there are two different types of exceptions that have slightly different rules.
class ArrayOutOfBounds { public static void main( String args[] ) { int students[] = new int[5]; try { System.out.println( "Start Try" ); students[ 10 ] = 1; System.out.println( "After assignment statement" ); } // catch is the handler catch (ArrayIndexOutOfBoundsException e) { System.err.println( "OutOfBounds: " + e.getMessage()); } System.out.println( "After try " ); } }Output Start Try OutOfBounds: 10 After try
try-catch must be together The try-catch block is part of one construct. They come together. The next three slides show the compile errors that occur when you don't keep the try-catch blocks together.
try and catch are not separate blocks
try and catch are one construct
class NeedCatchWithTryBlock { public static void main( String args[] ) { int students[] = new int[5]; try { System.out.println( "Start Try" ); students[ 10 ] = 1; System.out.println( "After assignment statement" ); } System.out.println( " After try " ); // Compile error } }Compile Error NeedCatchWithTryBlock.java:15: 'try' without 'catch' or 'finally'. System.out.println( " After try " );
try and catch must be together try and catch are not separate blocks
try and catch are one construct
public class NeedTryWithCatch { public static void main( String args[] ) { int students[] = new int[5]; System.out.println( "Start Try" ); students[ 10 ] = 1; System.out.println( "After assignment statement" ); // catch is the handler catch (ArrayIndexOutOfBoundsException e) { System.err.println( "OutOfBounds: " + e.getMessage()); } System.out.println( "After try " ); } }Compile Error 'catch' without 'try'. catch (ArrayIndexOutOfBoundsException e)
try and catch must be together try and catch are not separate blocks
try and catch are one construct
public class CanNotHaveCodeBetweenTryAndCatch { public static void main( String args[] ) { int students[] = new int[5]; try { System.out.println( "Start Try" ); students[ 10 ] = 1; System.out.println( "After assignment statement" ); } int DontHaveCodeHere = 5 / 32; // Error here // catch is the handler catch (ArrayIndexOutOfBoundsException e) { System.err.println( "OutOfBounds: " + e.getMessage()); } System.out.println( "After try " ); } }Compile Error 'try' without 'catch' or 'finally'. int DontHaveCodeHere = 5 / 32; 'catch' without 'try'. catch (ArrayIndexOutOfBoundsException e)
Exception Methods
The following methods, inherited from Throwable, are available for exceptions. The following slide shows example use and result of some of the methods. The most commonly used are toString, getMessage and the printStackTrace() methods. When we cover Java IO we will see how to use the PrintStream and PrintWriter.
fillInStackTrace()
public class ExceptionMethods { public static void main( String args[] ) throws Exception { try { testCall(); } catch ( BoringLectureException aLecture ) { aLecture.printStackTrace(); System.out.println( "toString:\n\t" + aLecture.toString() ); System.out.println( "message:\n\t" + aLecture.getMessage() ); System.out.println( "localized:\n\t" + aLecture.getLocalizedMessage() ); } } public static void testCall() throws BoringLectureException { throw new BoringLectureException( "Hi Mom" ); } } class BoringLectureException extends Exception { public BoringLectureException() { super(); } public BoringLectureException( String errorMessage ) { super( errorMessage ); } }
BoringLectureException: Hi Mom at ExceptionMethods.testCall(ExceptionMethods.java) at ExceptionMethods.main(ExceptionMethodsjava) toString: BoringLectureException: Hi Mom message: Hi Mom localized: Hi Mom
Checked & Unchecked Exceptions
The rules are slightly different for the two types of exceptions: Checked and Unchecked
Checked
If a checked exception is thrown in a method, the method must either:
Unchecked Exception Example
Unchecked Exceptions: Search the stack for the first enclosing try block
This example shows an unchecked exception being thrown out of one method to another. ArrayIndexOutOfBoundsException is an uncheck exception.
public class FindHandlerSimpleExample { public static void change( int[] course ) { System.out.println( "Start Change" ); course[ 10 ] = 1; System.out.println( "End Change" ); } public static void main( String args[] ) { int students[] = new int[5]; try { System.out.println( "Start Try" ); change( students ); System.out.println( "After change" ); } catch (ArrayIndexOutOfBoundsException e) { System.err.println( "OutOfBounds: " + e.getMessage()); } System.out.println( "After try " ); } }Output Start Try Start Change OutOfBounds: 10 After try
Unchecked Exception through Two methods
This example shows an unchecked exception being thrown out of one method through another and being caught in a third. ArrayIndexOutOfBoundsException is an uncheck exception.
public class FindHandlerDoubleExample { public static void doubleChange( int[] course ) { System.out.println( "Start Double Change" ); course[ 10 ] = 1; System.out.println( "End Double Change" ); } public static void change( int[] course ) { System.out.println( "Start Change" ); doubleChange( course ); System.out.println( "End Change" ); } public static void main( String args[] ) { int students[] = new int[5]; try { System.out.println( "Start Try" ); change( students ); System.out.println( "After change" ); } catch (ArrayIndexOutOfBoundsException e) { System.err.println( "OutOfBounds: " + e.getMessage()); } System.out.println( "After try " ); } }Output Start Try Start Change Start Double Change OutOfBounds: 10 After try
Unchecked Exception - Multiple try-catches
Once an exception is handled, the program continues normal execution after the try-catch block. Although this program has two try-catches for the ArrayIndexOutOfBoundsException, only the first is used.
public class FindHandlerTwoTryBlocks { public static void change( int[] course ) { try{ System.out.println( "Start Change" ); course[ 10 ] = 1; System.out.println( "End Change" ); } catch (ArrayIndexOutOfBoundsException e) { System.err.println( "Change Catch: " + e.getMessage()); } } public static void main( String args[] ) { int students[] = new int[5]; try { System.out.println( "Start Try" ); change( students ); System.out.println( "After change" ); } catch (ArrayIndexOutOfBoundsException e) { System.err.println( "Main Catch: " + e.getMessage()); } System.out.println( "After try " ); } }Output Start Try Start Change Change Catch: 10 After change After try
Unchecked Exceptions - No try-catch
As many of you have already found out, if there is no try-catch in your program, the JVM's try-catch catches the exception, prints out the stack trace, then exits.
public class NoHandler { public static void change( int[] course ) { System.out.println( "Start Change" ); course[ 10 ] = 1; System.out.println( "End Change" ); } public static void main( String args[] ) { int students[] = new int[5]; System.out.println( "Start Try" ); change( students ); System.out.println( "After change" ); } }Output Start Try Start Change java.lang.ArrayIndexOutOfBoundsException: 10 at NoHandler.main(NoHandler.java)
Checked Exceptions
Check Exceptions must be either:
import java.io.IOException; public class CheckedExceptionDeclaredInMethod { public static void readSomething() throws IOException { throw new IOException( "A checked exception" ); } public static void compileError() { throw new IOException( "A checked exception" ); } public static void main( String args[] ) { try { System.out.println( "Start Try" ); readSomething(); System.out.println( "After readSomething" ); } catch (IOException e) { System.err.println( "IO error: " + e.getMessage()); } System.out.println( "After try " ); } }
IOException as an Example
The next several slides use IOException as an example. This is done using the method sdsu.io.ASCIIInputStream.readInt(). That method acts like is was defined as given below. The actual situation is slightly more complex.
sdsu.io.ASCIIInputStream readInt() method
public int readInt() throws IOException { //blah // more blah if (bad condition) throw new IOException( "Bad Boy"); }
Checked exceptions - Catching in the Method
input.readInt() throws the checked exception IOException and states it throws the exception. Since we use input.readInt() in readSomething() there are two options: catch the exception or state that the method throws the exception. This example shows the first option.
import sdsu.io.ASCIIInputStream; import java.io.IOException; public class CheckedExceptionHandledInMethod { public static void readSomething() { try { System.out.println( "Please type an integer: " ); int youTyped; ASCIIInputStream input; input = new ASCIIInputStream(System.in); youTyped = input.readInt(); // throws IOException } catch (IOException e) { System.err.println( "IO error: " + e.getMessage()); } } public static void main( String args[] ) { System.out.println( "Start Try" ); readSomething(); System.out.println( "After readSomething" ); } }Output Start Try Please type an integer: After readSomething
Checked Exceptions - Not like Unchecked
input.readInt() throws the checked exception IOException and states it throws the exception. Since we use input.readInt() in readSomething() there are two options: catch the exception or state that the method throws the exception. This example shows what happens when we try to avoid using both options.
import sdsu.io.ASCIIInputStream; import java.io.IOException; public class IfYouDontDeclaredInMethod { public static void readSomething() { System.out.println( "Please type an integer: " ); int youTyped; ASCIIInputStream input = new ASCIIInputStream(System.in); youTyped = input.readInt(); // throws IOException } public static void main( String args[] ) { try { System.out.println( "Start Try" ); readSomething(); System.out.println( "After readSomething" ); } catch (IOException e) { System.err.println( "IO error: " + e.getMessage()); } System.out.println( "After try " ); } }Compile Error Error : Exception java.io.IOException must be caught, or it must be declared in the throws clause of this method. line 12 youTyped = input.readInt(); Error : Exception java.io.IOException is never thrown in the body of the corresponding try statement. line 22 catch (IOException e)
Checked Exceptions - Rethrowing
input.readInt() throws the checked exception IOException and states it throws the exception. Since we use input.readInt() in readSomething() there are two options: catch the exception or state that the method throws the exception. This example shows using both options. First, we catch the exception, then rethrow it. This is done when readSomething has to do some clean up, but can not complete its task when the exception is thrown. So it catches the exception to do the clean up. Then it rethrows the exception to let its caller know that the exception occurred and it could not complete its task properly.
import sdsu.io.ASCIIInputStream; import java.io. IOException; public class SometimesItMakesSenseToDoBoth { public static void readSomething() throws IOException { try { System.out.println( "Please type an integer: " ); int youTyped; ASCIIInputStream input; input = new ASCIIInputStream(System.in); youTyped = input.readInt(); // throws IOException } catch (IOException e) { System.err.println( "IO error: " + e.getMessage()); throw e; } } public static void main( String args[] ) { try { readSomething(); } catch (IOException e) { System.err.println( "Error: " + e.getMessage()); } System.out.println( "After try " ); } }
Multiple Exceptions
One try block can contain multiple catches. Integer divide by zero throws an ArithmeticException. (Note floating-point divide by zero does not throw an exception, but instead results in infinity or NaN depending on the value of the numerator.) Below we have a try block with two catches.
public class MultipleExceptions { public static void main( String args[] ) { int students[] = new int[5]; int classSize = 0; try { System.out.println( "Start Try" ); int classAverage = 10 / classSize; System.out.println( "After class average" ); students[ 10 ] = 1; System.out.println( "After array statement" ); } catch (ArrayIndexOutOfBoundsException e) { System.err.println( "OutOfBounds: " + e.getMessage()); } catch ( ArithmeticException e ) { System.err.println( "Math Error " + e.getMessage() ); } System.out.println( "After try " ); } }Output Start Try Math Error divide by zero After try
One Size Fits All Exception
A "catch (Type x )" will catch exceptions objects from class Type or any subclass of Type. Hence, "catch (Exception x )" will catch any exception. Doing this looses information about the actual class of the exception. Using "catch (Exception x )" is considered bad practice.
public class MultipleExceptions { public static void main( String args[] ) { int students[] = new int[5]; int classSize = 0; try { System.out.println( "Start Try" ); students[ 10 ] = 1; System.out.println( "After array statement" ); int classAverage = 10 / classSize; } catch ( Exception e ) // Not a good idea { System.err.println( "Exception:" + e.getMessage()); e.printStackTrace(); } System.out.println( "After try " ); } }Output Start Try Exception:10 java.lang.ArrayIndexOutOfBoundsException: 10 at MultipleExceptions.main(MultipleExceptions.java:11) After try
Beware of Order
A try block can have multiple catch blocks and a single catch block can catch more than one type of exceptions. Thus, it is possible for an exception to be potentially handled by more than one catch block in a try block. In this case, the first (or top) catch block will allows handle the exception. Thus, the order of the catch block can be important. Since catch ( Exception e ) catches all exceptions, it should be the last catch block in a try statement. If is not, any catch block following it will never be used. Java does not like programs to have unreachable code. This is a compile error.
public class MultipleExceptions { public static void main( String args[] ) { int students[] = new int[5]; int classSize = 0; try { System.out.println( "Start Try" ); students[ 10 ] = 1; System.out.println( "After array statement" ); int classAverage = 10 / classSize; } catch ( Exception e ) { System.err.println( "Exception:" + e.getMessage()); e.printStackTrace(); } catch (ArrayIndexOutOfBoundsException e) { System.err.println( "OutOfBounds: " + e.getMessage()); } System.out.println( " After try " ); } }Compile Error MultipleExceptions.java:21: catch not reached. } catch (ArrayIndexOutOfBoundsException e) { ^ 1 error
Select the First Exception That Applies
The first (or top) catch block that matches an exception will handle the exception.
public class MultipleExceptions { public static void main( String args[] ) { int students[] = new int[5]; int classSize = 0; try { System.out.println( "Start Try" ); students[ 10 ] = 1; System.out.println( "After array statement" ); int classAverage = 10 / classSize; } catch (ArrayIndexOutOfBoundsException e) { System.err.println( "OutOfBounds: " + e.getMessage()); } catch ( Exception e ) { System.err.println( "Exception:" + e.getMessage()); e.printStackTrace(); } System.out.println( "After try " ); } }Output Start Try OutOfBounds: 10 After try
What happens after the try-catch is done?
The line of code after the try-catch is executed. Normal program execution continues after the try-catch is done. ("Finally" blocks change this slightly.) There is no way to automatically restart the code that caused the exception. If you wish to retry the code that caused the exception, you need to recall the method/code.
public class ArrayOutOfBounds { public static void main( String args[] ) { int students[] = new int[5]; try { System.out.println( "Start Try" ); students[ 10 ] = 1; System.out.println( "After assignment statement" ); } // catch is the handler catch (ArrayIndexOutOfBoundsException e) { System.err.println( "OutOfBounds: " + e.getMessage()); } System.out.println( "After try " ); } }Output Start Try OutOfBounds: 10 After try
Final Block - For Clean Up
A try statement can have one finally block. Once you enter a try block, the finally block is always executed. The finally block is executed whether or not an exception is thrown. There is no way to avoid the finally block once the try block is started. This makes the finally block useful for insuring something will be done, like clean up.
public class FinalBlockWithExceptionCalled { public static void main( String args[] ) { int students[] = new int[5]; try { System.out.println( "Start Try" ); students[ 10 ] = 1; System.out.println( "After array statement" ); } catch (ArrayIndexOutOfBoundsException e) { System.err.println( "OutOfBounds: " + e.getMessage()); } finally { System.out.println( "In Final Block" ); } System.out.println( "After try " ); } }
Output OutOfBounds: 10 In Final Block After try
Final Block - Always Called
Although an exception is not thrown the finally bock is still done.
public class FinalBlockExceptionNotCalled { public static void main( String args[] ) { int students[] = new int[5]; try { System.out.println( "Start Try" ); students[ 2 ] = 1; System.out.println( "After array statement" ); } catch (ArrayIndexOutOfBoundsException e) { System.err.println( "OutOfBounds: " + e.getMessage()); } finally { System.out.println( "In Final Block" ); } System.out.println( "After try " ); } }Output Start Try After array statement In Final Block After try
Can't Escape Finally
Although a catch block is executed and tells the program to exit the method, the finally bock is still done.
public class ExceptionFunction { public static boolean change( int[] course ) { try { System.out.println( "Start change" ); course[ 10 ] = 1; System.out.println( "End Change" ); } catch (ArrayIndexOutOfBoundsException e) { System.err.println( "OutOfBounds: " ); return true; } finally { System.out.println( "In Final Block" ); } return false; } public static void main( String args[] ) { int students[] = new int[5]; System.out.println( "Main" ); System.out.println( change( students ) ); System.out.println( "After Change" ); } }Output Main Start Change OutOfBounds: In Final Block true After Change
Finally is Master
A finally block is entered with a reason, such as end of the try block or an exception was thrown. If the finally block creates it own reason to leave ( throws an exception, executes a return or break) then the original reason is forgotten. In this example an ArrayIndexOutOfBoundsException occurs. On the way to find the try-catch block to handle the exception, the finally block is executed. The finally block has a return statement. So, the ArrayIndexOutOfBoundsException is forgotten, and the program does a normal return from the explainThis method. Note we can have a try block without a catch if it has a finally block.
public class FinalExample { public static void explainThis() { int students[] = new int[5]; try { System.out.println( "Start Try" ); students[ 10 ] = 1; } finally { System.out.println( "In Final Block" ); return; } } public static void main( String args[] ) { try { explainThis(); } catch (ArrayIndexOutOfBoundsException missed) { System.out.println( "In ArrayIndexOutOfBoundsException" ); } System.out.println( "After try in main" ); } }Output Start Try In Final Block After try in main
Exceptions inside Exceptions
If an exception occurs in a catch block, the current try-catch is not used to find the try-catch to handle the exception. If that were done, it could lead to an infinite loop.
public class ExceptionFunction { public static boolean change( int[] course ) { try { System.out.println( "Start change" ); course[ 10 ] = 1; } catch (ArrayIndexOutOfBoundsException e) { course[ 10 ] = 1; System.err.println( "OutOfBounds: " ); return true; } finally { System.out.println( "In Final Block" ); } return false; } public static void main( String args[] ) { try { int students[] = new int[5]; System.out.println( "Main" ); System.out.println( change( students ) ); System.out.println( "After Change" ); } catch (ArrayIndexOutOfBoundsException e) { System.out.println( "Over Here" ); } } }Output Main Start change In Final Block Over Here
Exceptions and Inheritance
The rule A method that overrides another method may not be declared to throw more checked exceptions than the overridden method
The Details Let class Child be a subclass of class Parent
Let foo be a method of Parent that is overridden by child
If Child.foo has a throws clause that mentions any checked exceptions, then Parent.foo must have a throws clause.
For every checked exception listed in Child.foo, that same exception class or one of its superclasses must occur in the throws clause of Parent.foo
class Parent { public void foo() throws IOException {} public void bar() {} } class Child extends Parent { public void foo() {} // OK public void bar() throws IOException{} // Compile Error }
Tips on Using Exceptions
1. Use exceptions for exceptional conditions, not simple tests
Good
Stack accountHistory; if ( accountHistory.isEmpty() == false ) accountHistory.pop();Not so Good
try { accountHistory.pop(); } catch ( EmptyStackException doNothing ) { }
A try for every line, and every line for a Try?
2. Don't Micro manage Exceptions
Bad try { amount = accountHistory.pop(); } catch ( EmptyStackException doNothing ) { amount = -1; } try { out.writeFloat( amount ); } catch ( IOException ioError ) { System.err.out( "problem writing to file" ); }
Better try { amount = accountHistory.pop(); out.writeFloat( amount ); } catch ( EmptyStackException doNothing ) { } catch ( IOException ioError ) { System.err.out( "problem writing to file" ); }
Free Speech for Exceptions!
3. Don't Squelch Exceptions
Bad
try { amount = accountHistory.pop(); out.writeFloat( amount ); // a bunch more code that can throw various exceptions } catch ( Exception dontBotherMeWithExceptions ) { }
4. Catch Exceptions
public static void main( String[] args ) throws Exception { // Lots of stuff here }
List of All Java 1.0 Built-in Exceptions
AWTException | InstantiationException |
ArithmeticException | InterruptedException |
ArrayIndexOutOfBoundsException | InterruptedIOException |
ArrayStoreException | MalformedURLException |
ClassCastException | NegativeArraysizeException |
ClassNotFoundException | NoSuchElementException |
CloneNotSupportedException | NoSuchMethodException |
EOFException | NullPointerException |
EmptyStackException | NumberFormatException |
Exception | ProtocolException |
FileNotFoundException | RuntimeException |
IOException | SecurityException |
IllegalAccessException | SocketException |
IllegalArgumentException | StringIndexOutOfBoundsException |
IllegalMonitorStateException | UTFDataFormatException |
IllegalThreadStateException | UnknownHostException |
IndexOutOfBoundsException | UnknownServiceException |
AWTError | LinkageErrorNoClassDefFoundError |
AbstractMethodError | NoSuchFieldErrorNoSuchMethodError |
ClassCircularityError | OutOfMemoryError |
ClassFormatErrorError | StackOverflowError |
IllegalAccessError | UnknownError |
IncompatibleClassChangeError | UnsatisfiedLinkErrorVerifyError |
InstantiationErrorInternalError | VirtualMachineError |