Exception Handling - Language - Java 8 Pocket Guide (2014)

Java 8 Pocket Guide (2014)

Part I. Language

Chapter 7. Exception Handling

An exception is an anomalous condition that alters or interrupts the flow of execution. Java provides built-in exception handling to deal with such conditions. Exception handling should not be part of normal program flow.

The Exception Hierarchy

As shown in Figure 7-1, all exceptions and errors inherit from the class Throwable, which inherits from the class Object.

Figure 7-1. Snapshot of the exception hierarchy

Checked/Unchecked Exceptions and Errors

Exceptions and errors fall into three categories: checked exceptions, unchecked exceptions, and errors.

Checked Exceptions

§ Checked exceptions are checked by the compiler at compile time.

§ Methods that throw a checked exception must indicate so in the method declaration using the throws clause. This must continue all the way up the calling stack until the exception is handled.

§ All checked exceptions must be explicitly caught with a catch block.

§ Checked exceptions include exceptions of the type Exception, and all classes that are subtypes of Exception, except for RuntimeException and the subtypes of RuntimeException.

The following is an example of a method that throws a checked exception:

// Method declaration that throws

// an IOException

void readFile(String filename)

throws IOException {

...

}

Unchecked Exceptions

§ The compiler does not check unchecked exceptions at compile time.

§ Unchecked exceptions occur during runtime due to programmer error (out-of-bounds index, divide by zero, and null pointer exception) or system resource exhaustion.

§ Unchecked exceptions do not have to be caught.

§ Methods that may throw an unchecked exception do not have to (but can) indicate this in the method declaration.

§ Unchecked exceptions include exceptions of the type RuntimeException and all subtypes of RuntimeException.

Errors

§ Errors are typically unrecoverable and present serious conditions.

§ Errors are not checked at compile time and do not have to be (but can be) caught/handled.

TIP

Any checked exceptions, unchecked exceptions, or errors can be caught.

Common Checked/Unchecked Exceptions and Errors

There are various checked exceptions, unchecked exceptions, and unchecked errors that are part of the standard Java platform. Some are more likely to occur than others.

Common Checked Exceptions

ClassNotFoundException

Thrown when a class cannot be loaded because its definition cannot be found.

IOException

Thrown when a failed or interrupted operation occurs. Two common subtypes of IOException are EOFException and FileNotFoundException.

FileNotFoundException

Thrown when an attempt is made to open a file that cannot be found.

SQLException

Thrown when there is a database error.

InterruptedException

Thrown when a thread is interrupted.

NoSuchMethodException

Thrown when a called method cannot be found.

CloneNotSupportedException

Thrown when clone() is called by an object that is not cloneable.

Common Unchecked Exceptions

ArithmeticException

Thrown to indicate that an exceptional arithmetic condition has occurred.

ArrayIndexOutOfBoundsException

Thrown to indicate index out of range.

ClassCastException

Thrown to indicate an attempt to cast an object to a subclass of which it is not an instance.

DateTimeException

Thrown to indicate problems with creating, querying, and manipulating date-time objects.

IllegalArgumentException

Thrown to indicate that an invalid argument has been passed to a method.

IllegalStateException

Thrown to indicate that a method has been called at an inappropriate time.

IndexOutOfBoundsException

Thrown to indicate that an index is out of range.

NullPointerException

Thrown when code references a null object but a nonnull object is required.

NumberFormatException

Thrown to indicate an invalid attempt to convert a string to a numeric type.

UncheckedIOException

Wraps an IOException with an unchecked exception.

Common Errors

AssertionError

Thrown to indicate that an assertion failed.

ExceptionInInitializeError

Thrown to indicate an unexpected exception in a static initializer.

VirtualMachineError

Thrown to indicate a problem with the JVM.

OutOfMemoryError

Thrown when there is no more memory available to allocate an object or perform garbage collection.

NoClassDefFoundError

Thrown when the JVM cannot find a class definition that was found at compile time.

StackOverflowError

Thrown to indicate that a stack overflow occurs.

Exception Handling Keywords

In Java, error-handling code is cleanly separated from error-generating code. Code that generates the exception is said to “throw” an exception, whereas code that handles the exception is said to “catch” the exception:

// Declare an exception

public void methodA() throws IOException {

...

throw new IOException();

...

}

// Catch an exception

public void methodB() {

...

/* Call to methodA must be in a try/catch block

** since the exception is a checked exception;

** otherwise methodB could throw the exception */

try {

methodA();

}catch (IOException ioe) {

System.err.println(ioe.getMessage());

ioe.printStackTrace();

}

}

The throw Keyword

To throw an exception, use the keyword throw. Any checked/unchecked exception and error can be thrown:

if (n == -1)

throw new EOFException();

The try/catch/finally Keywords

Thrown exceptions are handled by a Java try, catch, finally block. The Java interpreter looks for code to handle the exception, first looking in the enclosed block of code, and then propagating up the call stack to main() if necessary. If the exception is not handled on the main thread (i.e., not the Event Dispatch Thread [EDT]), the program exits and a stack trace is printed:

try {

method();

} catch (EOFException eofe) {

eofe.printStackTrace();

} catch (IOException ioe) {

ioe.printStackTrace();

} finally {

// cleanup

}

The try-catch Statement

The try-catch statement includes one try and one or more catch blocks.

The try block contains code that may throw exceptions. All checked exceptions that may be thrown must have a catch block to handle the exception. If no exceptions are thrown, the try block terminates normally. A try block may have zero or more catch clauses to handle the exceptions.

TIP

A try block must have at least one catch or finally block associated with it.

There cannot be any code between the try block and any of the catch blocks (if present) or the finally block (if present).

The catch block(s) contain code to handle thrown exceptions, including printing information about the exception to a file, giving users an opportunity to input correct information. Note that catch blocks should never be empty because such “silencing” results in exceptions being hidden, which makes errors harder to debug.

A common convention for naming the parameter in the catch clause is a set of letters representing each of the words in the name of the exception:

catch (ArrayIndexOutOfBoundsException aioobe) {

aioobe.printStackStrace();

}

Within a catch clause, a new exception may also be thrown if necessary.

The order of the catch clauses in a try/catch block defines the precedence for catching exceptions. Always begin with the most specific exception that may be thrown and end with the most general.

TIP

Exceptions thrown in the try block are directed to the first catch clause containing arguments of the same type as the exception object or superclass of that type. The catch block with the Exception parameter should always be last in the ordered list.

If none of the parameters for the catch clauses match the exception thrown, the system will search for the parameter that matches the superclass of the exception.

The try-finally Statement

The try-finally statement includes one try and one finally block.

The finally block is used for releasing resources when necessary:

public void testMethod() throws IOException {

FileWriter fileWriter =

new FileWriter("\\data.txt");

try {

fileWriter.write("Information...");

} finally {

fileWriter.close();

}

}

This block is optional and is only used where needed. When used, it is executed last in a try-finally block and will always be executed, whether or not the try block terminates normally. If the finally block throws an exception, it must be handled.

The try-catch-finally Statement

The try-catch-finally statement includes one try, one or more catch blocks, and one finally block.

For this statement, the finally block is also used for cleanup and releasing resources:

public void testMethod() {

FileWriter fileWriter = null;

try {

fileWriter = new FileWriter("\\data.txt");

fileWriter.write("Information...");

} catch (IOException ex) {

ex.printStackTrace();

} finally {

try {

fileWriter.close();

} catch (Exception e) {

e.printStackTrace();

}

}

}

This block is optional and is only used where needed. When used, it is executed last in a try-catch-finally block and will always be executed, whether or not the try block terminates normally or the catch clause(s) were executed. If the finally block throws an exception, it must be handled.

The try-with-resources Statement

The try-with-resources statement is used for declaring resources that must be closed when they are no longer needed. These resources are declared in the try block:

public void testMethod() throws IOException {

try (FileWriter fw = new FileWriter("\\data.txt"))

{

fw.write("Information...");

}

}

Any resource that implements the AutoClosable interface may be used with the try-with-resources statement.

The multi-catch Clause

The multi-catch clause is used to allow for multiple exception arguments in one catch clause:

boolean isTest = false;

public void testMethod() {

try {

if (isTest) {

throw new IOException();

} else {

throw new SQLException();

}

} catch (IOException | SQLException e) {

e.printStackTrace();

}

}

The Exception Handling Process

Here are the steps to the exception handling process:

1. An exception is encountered, which results in an exception object being created.

2. A new exception object is thrown.

3. The runtime system looks for code to handle the exception, beginning with the method in which the exception object was created. If no handler is found, the runtime environment traverses the call stack (the ordered list of methods) in reverse looking for an exception handler. If the exception is not handled, the program exits and a stack trace is automatically output.

4. The runtime system hands the exception object off to an exception handler to handle (catch) the exception.

Defining Your Own Exception Class

Programmer-defined exceptions should be created when those other than the existing Java exceptions are necessary. In general, the Java exceptions should be reused wherever possible:

§ To define a checked exception, the new exception class must extend the Exception class, directly or indirectly.

§ To define an unchecked exception, the new exception class must extend the RuntimeException class, directly or indirectly.

§ To define an unchecked error, the new error class must extend the Error class.

User-defined exceptions should have at least two constructors—a constructor that does not accept any arguments and a constructor that does:

public class ReportException extends Exception {

public ReportException () {}

public ReportException (String message, int

reportId) {

...

}

}

Printing Information About Exceptions

The methods in the Throwable class that provide information about thrown exceptions are getMessage(), toString, and printStackTrace(). In general, one of these methods should be called in the catch clause handling the exception. Programmers can also write code to obtain additional useful information when an exception occurs (i.e., the name of the file that was not found).

The getMessage() Method

The getMessage() method returns a detailed message string about the exception:

try {

new FileReader("file.js");

} catch (FileNotFoundException fnfe) {

System.err.println(fnfe.getMessage());

}

The toString() Method

This toString() method returns a detailed message string about the exception, including its class name:

try {

new FileReader("file.js");

} catch (FileNotFoundException fnfe) {

System.err.println(fnfe.toString());

}

The printStackTrace() Method

This printStackTrace() method returns a detailed message string about the exception, including its class name and a stack trace from where the error was caught, all the way back to where it was thrown:

try {

new FileReader("file.js");

} catch (FileNotFoundException fnfe) {

fnfe.printStackTrace();

}

The following is an example of a stack trace. The first line contains the contents returned when the toString() method is invoked on an exception object. The remainder shows the method calls, beginning with the location where the exception was thrown all the way back to where it was caught and handled:

java.io.FileNotFoundException: file.js (The system

cannot find the file specified)

at java.io.FileInputStream.open(Native Method)

at java.io.FileInputStream.(init)

(FileInputSteam.java:106)

at java.io.FileInputStream.(init)

(FileInputSteam.java:66)

at java.io.FileReader(init)(FileReader.java:41)

at EHExample.openFile(EHExample.java:24)

at EHExample.main(EHExample.java:15)