Java Exception Handling
An Exception is an unwanted or unexpected event which occurs at run time. These events disrupt the expected flow of your program and can be fatal is not caught and handled. Java is designed to create a special type of object, an exception object, when an exception occurs. This object contains important data related to the exception to help identify when, where and why the exception occurred.
Types of Errors
Generally, there are 3 types of errors in programming: Syntax, Logic and Runtime
Syntax / Compilation Errors
These errors are a result of a failure to comply with the syntax rules of the programming language. The compiler will flag all syntax errors at compile time allowing programmers to fix the issues.
Logic Errors
These errors occur when there is an issue with the functionality of the program like an unexpected behaviour or outcome. The compiler will often successfully compile the program making these types of errors very hard to find.
Runtime Errors / Exceptions
These happen when the program is running. Exceptions describe unexpected conditions or events.
Handling Simple Exceptions
In Java, when something is asked of your program that it cannot do (like access a file that does not exist or modify an array element that is beyond the range of the array) an error is reported by the system, or an exception is thrown. These can be terminal if they are not handled properly.
Some of common exceptions that Java handles are:
lang.ArrayIndexOutOfBoundsException: When a non-existent array index was accessed
lang.ClassCastException: When an attempt was made to cast an object to an illegal type
lang.NullPointerException: When an object reference was used without being first instantiated
lang.ArithmeticException: When an illegal arithmetic calculate was attempted
An exception is a class, so this allows us to create our own exceptions:
Public class CustomException extends Exception { }
Handling Simple Exception Example
An error will occur if our program tries to divide a number by 0. In this case our program will report the error and then terminate:
int numerator = 20; int denominator = 0; System.out.println(numerator / denominator);
Although no errors are reported by NetBeans, when we run this program there will be an exception:
run:
Exception in thread “main” java.lang.ArithmeticException: / by zero at javaapplication54.JavaApplication54.main(JavaApplication54.java:26)
BUILD FAILED (total time: 0 seconds)
Looking at the exception description we can see that the exception was caused by a divide by zero. We can also tell where the exception occurred, you can see it immediately after the name of the java file and colon. This is critical information when debugging your code as it points you directly to the error, or close to it in some cases.
Throwable
Exceptions are subclasses of the Throwable class, and there are three main types of Throwable:
Exception
These are recoverable errors
They must be explicitly thrown to the calling method
They are Checked, they must be caught or declared to be thrown:
Declare to be thrown: include throws followed by exception type after the method name, this passes the exception up the call stack
Caught: use try – catch
Examples are:
IOException (EOFException, FileNotFoundException)
SQLException
AWTException
InterruptedException
RuntimeException
These are usually caused by programming errors
They are automatically thrown to the calling method
They are Unchecked
Examples are:
IndexOutOfBoundsException (ArrayIndexOutOfBoundsException, StringIndexOutOfBoundsException)
ArithmeticExeption
NullPointerException
Error
These are often unrecoverable external errors that your program could not anticipate
They are automatically thrown to the calling method if created
They are Unchecked
Examples are:
VirtualMachineError (StackOverflowError, OutOfMemoryError)
ExceptionInitializerError
AssertionError
IOError
AWTError
REMEMBER:
Exceptions under Error and RuntimeException classes are unchecked
Everything else under Throwable is checked
Checked/Unchecked
Checked are the exceptions that are checked at compile time. If some code within a method throws a checked exception, then the method must either handle the exception or it must specify the exception using throws keyword.
Unchecked are the exceptions that are not checked at compiled time.
Declare an Exception using throws
By declaring that an exception may occur in a method you are effectively passing the exception up the call stack to the calling method. It does not deal with the exception; it simply moves it to another method.
For example, if your program writes to a file but the file does not exist, an IOException will be thrown. This is a checked exception which means the program will not compile unless the exception is dealt with by declaring it or using a try-catch.
To deal with this let us declare the exception:
public static void main(String[] args) { writeToFile(); } public static void writeToFile() throws IOException { File myFile = newFile("/mydocs/myFile.doc"); FileWriter fw = new FileWriter(myFile); }
By adding throws IOException after the method name, we are passing the error up the call stack to the method that called the writeToFile() method: the main method.
However, you still need to deal with this exception because if it occurs it will terminate your program.
Try/Catch
Java provides a try/catch option that gives programmers the ability to try to execute a code segment that may throw an exception, and if it does, to report or catch the error but not terminate the program.
To use a try-catch you need to surround the code that you think may cause an exception with a try code block and follow that with a catch code block that contains two arguments: the expected exception and the variable name that will store the details of the exception. We can then use the toString() method to display the contents of the variable errorMessage:
try { int numerator = 20; int denominator = 0; System.out.println(numerator / denominator); } catch (ArithmeticException errorMessage) { System.out.println(errorMessage.toString()); }
When we run this code, the output is:
run:
java.lang.ArithmeticException: / by zero
BUILD SUCCESSFUL (total time: 0 seconds)
Critically the build was successful even though our code threw an exception. Java tried to execute the code within the try code block and encountered an error that would throw an exception, but since it was within a try code block Java ignored the try code block and executed the catch code block which contained statements that we wish to run if an exception was thrown.
We could also use the printStackTrace() method to display the error in a little more detail:
try { int numerator = 20; int denominator = 0; System.out.println(numerator / denominator); } catch (ArithmeticException errorMessage) { errorMessage.printStackTrace(); }
The output is:
run:
java.lang.ArithmeticException: / by zero at javaapplication54.JavaApplication54.main(JavaApplication54.java:27)
BUILD SUCCESSFUL (total time: 0 seconds)
There is no requirement to output the error detail, so if you leave the catch code block empty the program will still try & catch and continue executing the remainder of your program.
If the try code block did not throw an exception, then java would execute the try code block and ignore the catch code block. Let us try that by dividing 20 by a 2:
try { int numerator = 20; int denominator = 2; System.out.println(numerator / denominator); } catch (ArithmeticException errorMessage) { System.out.println(errorMessage.toString()); }
Now the output displays the result of the calculation since 20/2 is legal, and the catch code block was ignored:
run:
10
BUILD SUCCESSFUL (total time: 0 seconds)
Multiple catch blocks
You can have multiple catch blocks if you need to check for multiple exceptions, for example an IOException and a FileNotFoundException. Care must be taken to ensure the catch blocks are in the correct order if you use sub-classes of a super-class.
For example, IOException is the super-class of the FileNotFoundException class. When constructing catches for these exceptions, the sub-class must be first, followed by the super-class:
public static void main(String[] args) { writeToFile(); } public static void writeToFile() throws IOException { File myFile = newFile("/mydocs/myFile.doc"); try { FileWriter fw = new FileWriter(myFile); } catch (FileNotFound fnf) { System.out.println(fnf.getClass()); } catch (IOException ioe) { ioe.printStackTrace(); } }
This is because the super-class will act as a catch-all for all exceptions.
catch (Exception e)
This is known as an empty catch clause or suppressing the exception, this will catch any exception without identifying what type of exception it is.
try { File file = new File("my/file/path"); FileInputStream fileInputStream = new FileInputStream(file); //These lines cause an exception } catch (Exception e) { //No matter what the exception is, this will catch it System.out.println("In exception catch block"); } //Once this is used, no further catch clauses can be used as any exceptions have already been caught
The finally block
It is good practice to include that finally block that will execute any code that must be executed after all the try and catch blocks have been executed. This might be to close a file that may have been opened, etc. The finally block will always execute, even if an exception is not thrown, unless the program is terminated before it is executed (e.g. with a System.exit(0); statement). The typical syntax is:
finally { statements; }
A catch block is not always required when using a finally block.