Exceptions and Advanced File I/O I
- Contents
- Exceptions
- Why Exceptions?
- Exception Classes
- Handling Exceptions
- Polymorphic References to Exceptions
- Handling Multiple Exceptions
- The
finally
Clause - The Stack Trace
- Uncaught Exceptions
- Checked and Unchecked Exceptions
- Throwing Exceptions
Exceptions
The Java programming language uses exceptions to handle runtime errors and other exceptional events.
An exception is an event that occurs during the execution of a program that disrupts the normal flow of instructions.
Exception are said to have been “thrown”.
It is the programmer’s responsibility to write code that detects and handles exceptions.
Unhandled exceptions will crash a program.
Book example:
Example code:
public class Program { public static void main(String[] args) { double aDouble = Double.parseDouble("1.2.3"); System.out.println("The double value: " + aDouble); } }
Java allows you to create exception handlers.
An exception handler is a section of code that gracefully reponds to exceptions.
Example code:
public class Program { public static void main(String[] args) { try { double aDouble = Double.parseDouble("1.2.3"); System.out.println("The double value: " + aDouble); } catch (NumberFormatException e) { System.err.println("Invalid double."); } } }
The process of intercepting and responding to exceptions is called exception handling.
The default exception handler deals with unhandled exceptions.
The default exception handler prints a message to Standard Error and immediately terminates the program.
Why Exceptions?
Separating Error-Handling Code from “Regular” Code
Consider the task of reading a file into memory:
readFile { openTheFile; determineFileSize; allocateMemory; readFileIntoMemory; closeTheFile; }
What can go wrong in this procedure:
- The file can’t be opened.
- The length of the file can’t be determined.
- Enough memory can’t be allocated.
- Read fails.
- The file can’t be closed.
Procedure with traditional error-management techniques:
int readFile { openTheFile; if (failedToOpenTheFile) return -1; determineFileSize; if (failedToDetermineFileSize) { closeTheFile; if (failedToCloseTheFile) return -5; return -2; } allocateMemory; if (faileToAllocateMemory) { closeTheFile; if (failedToCloseTheFile) return -6; return -3; } readFileIntoMemory; if (failedToReadFileIntoMemory) { closeTheFile; if (failedToCloseTheFile) return -7; return -4; } return 0; }
Exceptions enable you to write the main flow of your code and to deal with the exceptional cases elsewhere:
readFile { try { openTheFile; determineFileSize; allocateMemory; readFileIntoMemory; closeTheFile; } catch (openFileException) { // doSomething; } catch (sizeDeterminationException) { // doSomething; } catch (memoryAllocationException) { // doSomething; } catch (readFileException) { // doSomething; } catch (closeFileException) { // doSomething; } }
Propagating Errors Up the Call Stack
Suppose the
readFile
method is the fourth method in a series of nested method calls.method1 { method2; } method2 { method3; } method3 { readFile; }
With traditional error-notification techniques:
method1 { int error = method2; if (error) doErrorProcessing; else proceed; } int method2 { int error = method3; if (error) return error; else proceed; } int method3 { int error = readFile; if (error) return error; else proceed; }
With exceptions, a method can ignore any exceptions thrown within it, and allow a method further up the call stack to process the error:
method1 { try { method2; } catch (exception) { doErrorProcessing; } } method2 throws exception { method3; } method3 throws exception { readFile; }
Grouping and Differentiating Error Types
Because all exceptions thrown within a program are objects, the grouping or categorizing of exceptions is a natural outcome of the class hierarchy.
For example, the
FileNotFoundException
is a type ofIOException
.The
FileNotFoundException
class has no descendants, so the following handler can handle only one type of exception.catch (FileNotFoundException e) { // ... }
To catch all I/O exceptions, regardless of their specific type, an exception handler specifies an
IOException
argument.catch (IOException e) { // ... }
It is also possible to set up an exception handler that handles any exception.
catch (Exception e) { // ... }
In most situations, however, you want exception handlers to be as specific as possible.
Exception Classes
An exception is an object.
Exception objects are created from classes in the Java API hierarchy of exception classes.
All of the exception classes in the hierarchy are derived from the
Throwable
class.Error
andException
are derived from theThrowable
class.Classes that are derived from
Error
are for exceptions that are thrown when critical system-wide errors occurs.- An internal error in the Java Virtual Machine
- Running out of memory
Applications should not try to handle these errors because they are the result of an error beyond the control of the application.
Programmers should handle most of the exceptions that are instances of classes that are derived from the
Exception
class.
Handling Exceptions
To handle an exception, we use a
try
statement.try { // code that may throw an exception. : } catch (ExceptionType parameterName) { // code to deal with the exception. : }
First the keyword
try
indicates a block of code will be attempted. Curly braces are required to surround this block of statements.This block of code is known as a try block.
A try block
- is one or more statements that are executed
- can potentially throw an exception
After the try block, a
catch
clause appears.A catch clause begins with the keyword
catch
.- The
ExceptionType
is the name of an exception class. parameterName
is a variable name that will reference the exception object if the code in the try block throws an exception that can be assigned toExceptionType
.
- The
The JVM searches up the call stack for a
catch
clause that can deal with the exception.Book example:
The parameter must be of a type that is compatible with the thrown exception’s type.
After an exception is caught, the program will continue execution at the point just past the catch block.
Example code:
import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; public class Program { public static void main(String[] args) { try { File file = new File("missing.txt"); Scanner scanner = new Scanner(file); while (scanner.hasNextLine()) System.out.println(scanner.nextLine()); System.out.println("printing inside of try..."); } catch (FileNotFoundException e) { System.err.println("File not found."); } System.out.println("printing outside of try..."); } }
Each exception object has a method named
getMessage
that can be used to retrieve the default error message for the exception.Book example:
Example code:
import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; public class Program { public static void main(String[] args) { try { File file = new File("missing.txt"); Scanner scanner = new Scanner(file); while (scanner.hasNextLine()) System.out.println(scanner.nextLine()); } catch (FileNotFoundException e) { System.err.println(e.getMessage()); } } }
Polymorphic References to Exceptions
When handling exceptions, we can use a polymorphic reference as a parameter in the
catch
clause.A
catch
clause that uses a parameter variable of theException
type is capable of catching any exception that derived from theException
class.Example code:
import java.io.File; import java.util.Scanner; public class Program { public static void main(String[] args) { try { // task 1: open a file and print the contents. File file = new File("myFile.txt"); Scanner scanner = new Scanner(file); while (scanner.hasNextLine()) System.out.println(scanner.nextLine()); // task 2: parse the first command-line argument. int num = Integer.parseInt(args[0]); System.out.println("The argument is: " + num); } catch (Exception e) { System.err.println("Error encountered."); } } }
The
Integer
class’sparseInt
method throws aNumberFormatException
object.The
NumberFormatException
class is derived from theException
class.
Handling Multiple Exceptions
The code in the try block may be capable of throwing more than one type of exception.
Programmers can write a
catch
clause for each type of exception that could potentially be thrown.The JVM will run the first compatible
catch
clause.The
catch
clause, therefore, must be listed from most specific to most general.Book example:
Example code:
import java.util.InputMismatchException; import java.util.NoSuchElementException; import java.util.Scanner; public class Program { public static void main(String[] args) { try { Scanner scanner = new Scanner(System.in); System.out.print("Please type a interger here: "); int num = scanner.nextInt(); System.out.println("The user input integer is: " + num); } catch (InputMismatchException e) { System.err.println("Input Mismatch"); } catch (NoSuchElementException e) { System.err.println("No Such Element"); } catch (IllegalStateException e) { System.err.println("Illegal State"); } } }
A try statement may have only one
catch
clause for each specific type of exception.try { : } catch (Exception e) { : } catch (Exception e) { : }
Error: Unreachable catch block for
Exception
. It is already handled by the catch block forException
.The
InputMismatchException
class is derived fromNoSuchElementException
class.try { : } catch (NoSuchElementException e) { : } catch (InputMismatchException e) { : }
Error: Unreachable catch block for
InputMismatchException
. It is already handled by the catch block forNoSuchElementException
. To correct this error, we do:try { : } catch (InputMismatchException e) { : } catch (NoSuchElementException e) { : }
Similarly, the
NumberFormatException
class is derived fromIllegalArgumentException
class.try { : } catch (IllegalArgumentException e) { : } catch (NumberFormatException e) { : }
Error: Unreachable catch block for
NumberFormatException
. It is already handled by the catch block forIllegalArgumentException
. To correct this error, we do:try { : } catch (NumberFormatException e) { : } catch (IllegalArgumentException e) { : }
The finally
Clause
The try statement may have an optional
finally
clause.If present, the
finally
clause must appear after all of thecatch
clauses.The finally block has one or more statements.
- These statements always execute after the try block has executed.
- These statements always execute after any catch blocks have executed if any exceptions had occurred.
The statements in the finally block execute whether an exception occurs or not.
public class Program { public static void main(String[] args) { try { System.out.println("1. try"); Integer.parseInt("This will not parse!"); System.out.println("This will not print!"); } catch (NumberFormatException e) { System.out.println("2. catch"); } finally { System.out.println("3. finally"); } System.out.println("4. done"); } }
Note: If a catch clause invokes
System.exit()
the finally clause will not execute.
The Stack Trace
The stack trace is an internal list describing all methods that are currently executing for one thread.
It indicates:
- The method that was executing when an exception occurred.
- All methods that were called in order to execute that method that invoked the exception.
Book example:
Example code:
public class Program { public static void main(String[] args) { System.out.println("Calling myMethod..."); try { myMethod(); } catch (Exception e) { System.err.println("Stack Trace: "); e.printStackTrace(); } System.out.println("Method main is done."); } private static void myMethod() { System.out.println("Calling produceError..."); produceError(); System.out.println("myMethod is done."); } private static void produceError() { String str = "abc"; System.out.println(str.charAt(3)); System.out.println("produceError is done."); } }
Uncaught Exceptions
When an exception is thrown, it cannot be ignored.
It must be handled by the program or by the default exception handler.
When the code in a method throws an exception:
- Normal execution of that method stops.
- The JVM searches for a compatible exception handler for the corresponding try block, if there is one.
If there is no compatible exception handler inside the method:
- Control of the program is passed to the previous method in the call stack.
- If that method has no compatible exception handler, then control is passed again, up the call stack, to the previous method.
If control reaches the
main
method:- The main method could handle the exception.
- Or the program is halted and the default exception handler handles the exception.
Example code:
public class Program { public static void main(String[] args) { System.out.println("Calling myMethod..."); myMethod(); System.out.println("Method main is done."); } private static void myMethod() { System.out.println("Calling produceError..."); produceError(); System.out.println("myMethod is done."); } private static void produceError() { final String str = "abc"; System.out.println(str.charAt(3)); System.out.println("produceError is done."); } }
Checked and Unchecked Exceptions
There are two categories of exceptions:
- Unchecked
- Checked
Unchecked exceptions are those that are derived from the
Error
class or theRuntimeException
class.Exceptions derived from
Error
are thrown when a critical system-wise error occurs, and should not be handled.RuntimeException
serves as a superclass for exceptions that result from programming errors.These exceptions can be avoided with properly written code.
Unchecked exceptions, in most cases, should not be handled.
All exceptions that are not derived from
Error
orRuntimeException
are checked exceptions.If the code in a method can throw a checked exception, the method:
- must handle the exception;
- or it must have a
throws
clause listed in the method header.
The
throws
clause informs the compiler what exceptions can be thrown from a method.Example code:
import java.io.File; import java.util.Scanner; public class Program { public static void main(String[] args) { File file = new File("myFile.txt"); // Compiling error: Unhandled exception type FileNotFoundException. Scanner scanner = new Scanner(file); while (scanner.hasNext()) { System.out.println(scanner.nextLine()); } } }
The code in this method is capable of throwing checked exceptions.
The keyword
throws
can be written at the end of the method header, followed by a list of the types of exceptions that the method can throw.public void myFunction() throws SomeException {...}
Throwing Exceptions
The Java language allows code to
- throw one of the standard Java exceptions.
- throw an instance of a custom exception class that a programmer has designed.
The
throw
statement is used to manually throw an exception.if (!isConditionGood) throw new ConditionBadException("Condition is bad.");
The argument string contains a custom error message that can be retrieved from the exception object’s
getMessage
method.- Note: Do not confuse the
throw
statement with thethrows
clause. Book example:
Example code:
public class Program { public static void main(String[] args) { System.out.println("Calling myMethod..."); try { myMethod(); } catch(StringIndexOutOfBoundsException e) { System.err.println(e.getMessage()); } System.out.println("Method main is done."); } private static void myMethod() throws StringIndexOutOfBoundsException { System.out.println("Calling produceError..."); produceError(); System.out.println("myMethod is done."); } private static void produceError() throws StringIndexOutOfBoundsException { String str = "abc"; System.out.println(str.charAt(3)); System.out.println("produceError is done."); } }