Exception Handling - 2020
In code writing, error detection and handling is one of the most important parts for robust application. An exception in Java signals the occurrence of unexpected error situation during the life cycle of a program, e.g., an array index is out of bounds, a requested file cannot be located, or a network link failed. Explicit checks for and deals with errors can easily bloat the source into incomprehensible spaghetti code.
Java arms developers with an exception handling mechanism for dealing with such error situation systematically. Exception handling allows developers to detect errors easily without writing special code to test return values. It lets us keep exception-handling code separated from the exception-generating code. It also lets us use the same exception-handling code to deal with a range of possible exceptions.
- exception
Exceptional condition.
It may alter the normal program flow. - thrown
When an exceptional event occurs, an exception is said to be thrown. - exception handler
The code that's responsible for doing something about the exception.
It catches the thrown exception.
The exception mechanism is built around the throw-and-catch paradigm. To throw an exception is to signal that an unexpected condition has occurred. To catch an exception is to take appropriate action to deal with the exception. An exception is caught by an exception handler. The runtime behavior of the program determines which exceptions are thrown and how they are caught.
In other words, Java's exception mechanism is based on "you knowing that the method you're calling is risky (i.e. that the method might generate an exception), so that you can write code to deal with the possibility. If you know you might get an exception when you call a particular method, you can be prepared for - possible even recover from - the problem that caused the exception." - from Head First Java.
Sample code looks like this:
try { doRiskyThing(); } catch (Exception e) { // try to recover // This code only runs if an Exception is thrown }
A try/catch block tells the compiler that we know an exceptional thing could happen in the method we're calling, and that we're prepared to handle it. Actually, the compiler doesn't care how we handle it. It cared only that we say we're taking care of it.
Because an Exception is an object, what we catch is an object. We'll get to it, but the Exception class extends class Throwable and inherits two key methods, getMessage() and printStackTrace().
catch (Exception e) {
The catch argument is declared as type Exception, and the parameter reference variable is e.
Here is another example.
public class Risky { class MyException extends Exception { } public static void main(String[] args) { Risky r = new Risky(); r.crossFingers(); System.out.println("Exit main()."); } public void doRisky() throws MyException{ System.out.println("doRisky()"); throw new MyException(); } public void crossFingers() { try { doRisky(); } catch (MyException e){ System.out.println("caught exception: " + e.getMessage()); e.printStackTrace(); } System.out.println("crossFingers()"); } }
The output is:
doRisky() caught exception: null crossFingers() Exit main(). Risky$MyException at Risky.doRisky(Risky.java:13) at Risky.crossFingers(Risky.java:18) at Risky.main(Risky.java:7)
One method will catch what another method throws. An exception is always thrown back to the caller. The method that throws has to declare that it might throw the exception.
The following line is declaring to tell the world that it throws MyException.
public void doRisky() throws MyException{
It creates a new Exception object and throws it.
throw new MyException();
If we can't recover from the exception, we should try to get a stack trace using the printStackTrace() method which inherited from the class Throwable.
e.printStackTrace();
Now, let's look at what's really happening after exception has been thrown.
public class Average { int[] scores = {90, 60, 30}; public static void main(String[] args) { Average a = new Average(); a.displayAverage(); System.out.println("Exit main()."); } public void displayAverage(){ double av = calculateAverage(); System.out.println("Average = " + av); System.out.println("Exit displayAverage()"); } public double calculateAverage(){ int size = scores.length; double sum = 0.0; for (int i = 0; i <= size; i++) { sum += scores[i]; } System.out.println("Exit calculateAverage()"); return sum/size; } }
Output is:
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3 at Average.calculateAverage(Average.java:21) at Average.displayAverage(Average.java:12) at Average.main(Average.java:7)
The program stopped at the point where exception was thrown. But still we got the line number where things gotten wrong, at least.
at Average.calculateAverage(Average.java:21)
Note that execution didn't reach output writing part of the code at all:
System.out.println("Exit calculateAverage()"); System.out.println("Exit displayAverage()"); System.out.println("Exit main().");
Let put try-catch block into the code.
public class Average { int[] scores = {90, 60, 30}; public static void main(String[] args) { Average a = new Average(); a.displayAverage(); System.out.println("Exit main()."); } public void displayAverage(){ try { double av = calculateAverage(); System.out.println("Average = " + av); } catch (Exception ae){ ae.printStackTrace(); } System.out.println("Exit displayAverage()"); } public double calculateAverage(){ int size = scores.length; double sum = 0.0; for (int i = 0; i <= size; i++) { sum += scores[i]; } System.out.println("Exit calculateAverage()"); return sum/size; } }
Then, we'll have slightly different output because normal execution continued after the exception.
java.lang.ArrayIndexOutOfBoundsException: 3 at Average.calculateAverage(Average.java:27) at Average.displayAverage(Average.java:13) at Average.main(Average.java:7) Exit displayAverage() Exit main().
ArrayIndexOutOfBoundsException is thrown at the line:
sum += scores[i];
when i = size. Then, the execution of the method calculateAverage() is stopped and the exception propagated into displayAverage(), where it is handled by the catch block. Normal execution of the method continued printing "Exit displayAverage()" after the try-catch construct, as witnessed by the output "Exit main()".
Although try and catch provide a mechanism for trapping and handling exceptions, we are left with the problem of how to clean up if exception occurs.
Because execution transfers out of the try block as soon as an exception is thrown, we can't put our clean up code at the bottom of the try block and expect it to be executed if an exception occurs.
A finally block encloses code that is always executed at some point after the try block, whether an exception was thrown or not. So, this is the right place to close our files, release our network sockets, and perform any other cleanup our code requires.
Here are pseudo codes:
try { // This is the first line of the "guarded region" } catch (MyException) { // Code to handle the exception } finally { // Code to release any resource }
The following code is legal even though it has no catch
try { } finally { }
But following code is illegal because try needs either catch or finally:
try { } // Here we need either a catch or finally.
Here is the summary:
It is illegal to use a try clause without either a catch or a finally clause. A try clause by itself will result in a compiler error. Any catch clauses must immediately follow the try block. Any finally clause must immediately follow the last catch clause (or it must immediately follow the try block if there is no catch). It is legal to omit either the catch clause or the finally clause, but not both..
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization