As developers, we deal with risky situations on a daily basis. Server goes down, or there isn’t enough space to allocate objects on the heap or file is not present in the given location and so on. So every time we decide to do a risky action, we have to inform the compiler that we know it is a risky thing and we are ready to handle it. How we treat it is not the compiler’s problem. All it needs to remember is that we are taking care of any exceptional situations that might arise.
We handle these situations by wrapping our code in try catch in Java.
The basic syntax of try/catch/finally blocks:
try{
//code that could throw an exception
//if exception thrown, following code is not reachable
//control jumps to catch block
}catch(ExceptionType referenceVariable){
//code that is executed only when an exception is thrown
//does something using the exception reference variable
//usually prints stack trace or exception description
}finally{
//cleanup code
//always executes regardless of an exception
}
Note
- No code can be written between the try block and catch block.
- Try block MUST be followed either by a catch or a finally block or both. And if there is no catch block, then the finally method should declare the exception though it has try/finally.
- You cannot have a catch or finally without a try block.
- If you don’t want to handle an exception in your code, then declare them with a throws clause. Whoever calls your code has to handle it with a try/catch block.
Flow control
- If try block succeeds, i.e. no exception is thrown, then control goes to the finally block if it is present. Catch blocks are skipped. In the absence of a finally block, any code that is below the catch block is executed.
- If try block fails (exception occurs), control transfers to the catch block where the exception is handled. The remaining code in the try block is never executed. In case a finally block is present, then that is run after the catch block’s execution completes.
- If try/catch blocks have a return statement, even then the finally block executes! Flow control first jumps to the finally block and then goes back to the return statement.
Example
public class TryCatch1 {
public static void main(String[] args) {
System.out.println(riskyAction());
}
public static String riskyAction(){
try{
System.out.println("Started executing try block");
return "returning from try block";
}catch(Exception e){
return "returning from catch blcok";
}finally{
System.out.println("print statement from finally");
}
}
}
Output
Started executing try block
print statement from finally
returning from try block
Explanation
- Try block runs and prints “Started executing try block”.
- Once it encounters a return statement, the flow immediately transfers to finally block and prints “print statement from finally”.
- Upon the completion of finally block execution, control goes back to the return statement in the try block and returns “returning from try block”.
- If finally block has a return statement, then the return statements from try/catch blocks will be overridden.
Example
public class TryCatch2{
public static void main(String[] args) {
System.out.println(riskyAction("hello"));
System.out.println("-----------");
System.out.println(riskyAction("howdy"));
}
public static String riskyAction(String greeting){
try{
if(greeting.equals("hello")){
System.out.println(greeting + " from try block");
}else{
throw new Exception();
}
return "returning from try block";
}catch(Exception e){
System.out.println(greeting + " from catch block");
return "returning from catch block";
}finally{
return "returning from finally block";
}
}
}
Output
hello from try block
returning from finally block
-----------
howdy from catch block
returning from finally block
Explanation
For the method call, riskyAction(“hello”): try block succeeds and prints “hello from try block”. Since it has a return statement, control transfers to finally block. Finally block also has a return statement which overrides the one from try block and thus “returning from finally block” is returned by the method and printed to console.
For riskyAction(“howdy”): try block throws an exception which is handled in the catch block which prints “howdy from catch block”. Just as we saw in the case of a try block success, here also the return statement from finally block overrides the return statement in the catch block. As a result, “returning from finally block” is returned by the method and printed to console.
Note: Since finally block always gets executed irrespective of the occurrence of an exception, if it has a return statement, unexpected results can be expected and might become difficult to debug. As a good practice, it is better to avoid writing return statements in finally block.
Catching multiple exceptions
Before Java 7, in order to handle more than one exception, multiple catch blocks were used ordered from most specific to most general. The code is written to print the stack trace, perform error recovery, to chain exceptions, allowing the user to make decisions, etc. But writing multiple catch blocks contain a lot of duplicate code. Also, programmers tend to catch broader or more general exceptions rather than the specific ones. Example, catching IOException instead of FileNotFoundException.
From Java SE 7 and later, these drawbacks are addressed with a single catch block that can handle more than one type of exception. Here the exception types to be handled are specified in the parathesis of the catch clause separated by a vertical bar (|).
Example,
catch(ArrayIndexOutOfBoundsException | SQLException ex){
ex.printStackTrace();
}
Note
- Whenever a single catch block handles more than one exception, the reference variable (‘ex’ in above example) is final and hence it is treated as a constant. Therefore no other values can be assigned to it. This limits the exception handling abilities in some cases.
- An exception type cannot be grouped with its superclass as the subclass exception would become unreachable as it will already be caught.
Example
A closer look at Finally block
- Finally block always executes once the control from the try block exits irrespective of whether an exception is thrown or not.
- Finally block is not executed,
- If JVM exits while try/catch block code is being executed
- If System.exit() is executed before control reaches finally block
- If the thread executing try/catch code is interrupted or killed
- Finally block prevents any resource leaks by closing any that might have been opened.
- If any resources are to be recovered, the code has to be placed in finally block.
- A try block with only a finally block, (i.e. no catch block) should still declare the exception in order to handle it.
In Java SE 7 and later, try-with-resources statement is considered for automatically closing the resources that are no longer in use. It is not required for the developer to remember to release the used resources by writing a finally block just for this very purpose.
The try-with-resources statement post has detailed information along with its advantages, takeaways, and example code snippets.
Can we write try-catch block inside finally block…
Yes absolutely you can.
amazinnng
Hi sir,
nice explanation..
Hi Sir,
This is exactly the information I was looking for! Thanks for highlighting do’s and don’ts which is what we want. Looking for more topic explanations like this. We all follow JBT and we are looking for quality topics like this.