In this tutorial I am going to explain Java exceptions in details and how to use them in the real applications. One of the important measure to assess the quality of your Java code is to review the exception handling and how exceptions scenarios are handled.
If you have to write an efficient exception handling, then you should have good understanding on the exception hierarchy and different categories of the exception. Let’s read this tutorial and share your feedback at the comments section.
What is an Exception?
Exceptions are events that occurs when executing the programs that will disrupt or terminate the normal flow of the program. In Java, an exception is an object that contains the type of the exception and details of the root cause for that exception.
- The exception can be JDK library exception (built-in with JDK API) or user defined custom exception.
- In general practice, we can use the
printStackTrace()
for printing the complete details of the exception. This method will be print the origin of the exception and path of the exception propagation. - The exceptions can be thrown because of programmatic errors or environment issues.
Exception Hierarchy
It is very important to understand the basic exception hierarchy of Java exception classes.
Throwable
is root class for the exception hierarchy.Error
andException
classes are inherited from theThrowable
class.RuntimeException
exception is the subclass ofException
class.
Exception Categories
There are three types of exception categories:
- Checked exceptions
- Runtime exceptions (unchecked exceptions)
- Errors
Checked Exceptions
A checked exception is an unacceptable condition foreseen by the author of the code but that would not occur immediately at the compile time. The exception probably may occur at the runtime in certain scenarios. For example : FileNotFountException
is a checked exception. This exception is thrown only when the file could not be found.
Look at the following code:
File file = new File("test.txt"); try{ FileInputStream fin = new FileInputStream(file); }catch (FileNotFoundException e){ e.printStackTrace(); }
In the above code, the author of the code knows that at runtime there is a chance for missing the file. This is anticipated and could be nice to handle those scenarios while writing the code. That is the reason handling the checked exceptions are compulsory and code will not compile if you are not handling the checked exceptions in your code.
- Checked exceptions extend the class
java.lang.Exception
. - Checked exceptions must be handled by the developer
- If you want to write your own checked exception, extend the class
java.lang.Exception
from your custom exception class.
Some of the example for the checked exceptions are:
- FileNotFoundException
- ParseException
- ClassNotFoundException
- CloneNotSupportedException
- InstantiationException
- InterruptedException
- NoSuchMethodException
- NoSuchFieldException
Runtime Exceptions
Runtime exceptions are the exceptions that are caused by the programming mistakes and not an expected one in the application. For example: Handling the null check for the objects before invoking any of its members are responsibility of developers. If you are not handling the null checks, you would encounter NullPointerException
while running the application. Note that you should not handle these exceptions in the code. Because, these exceptions has to be handled by the code itself.
Look at the bellow code snippet:
private static StringBuilder sb; public static void main(String args[]) { System.out.println(sb.toString()); }
In the above code, the variable sb
is not initialized after its declaration. Since it will have the default value of null
, it will throw NullPointerException
while accessing the methods. There will not be any compiler error at the compile, these errors are thrown only at the runtime. Hence, the developer should not handle these exceptions in code.
- All the runtime exceptions are the type of
java.lang.RuntimeException
. If you want to write your custom runtime exception, extend your custom class withjava.lang.RuntimeException
Few of the runtime exceptions are :
- ArrayStoreException
- BufferOverflowException
- BufferUnderflowException
- CannotRedoException
- CannotUndoException
- ClassCastException
- CMMException
- ConcurrentModificationException
- DOMException
- EmptyStackException
- IllegalArgumentException
- IllegalMonitorStateException
- IllegalPathStateException
- IllegalStateException
- ImagingOpException
- IndexOutOfBoundsException
- MissingResourceException
- NegativeArraySizeException
- NoSuchElementException
- NullPointerException
- ProfileDataException
- ProviderException
- RasterFormatException
- SecurityException
- SystemException
- UndeclaredThrowableException
- UnmodifiableSetException
- UnsupportedOperationException
Errors
An error is a serious exception thrown by the JVM as a result of an error in your application environment. This error is not controlled by your code. For example :NoClassDefFoundError
is thrown by JVM when it is not able to locate the class file that it is suppose to load for running your application. Another example is StackOverflowError
when JVM running out of memory in for stack for the Java program. These conditions are related to the environment (JVM, JRE, etc.) hence nowhere in control of your code. When your application encounters this kind of errors, your application will be immediately terminated.
Look at the below code:
public class ExceptionExample { public ExceptionExample(){ ExceptionExample example = new ExceptionExample(); } public static void main(String args[]) { ExceptionExample example = new ExceptionExample(); } }
If you are running the above code, you will be getting the following error and the program will be terminated. Your class can catch and handle the errors, but it shouldn’t handle the errors. Let JVM itself handle the errors.
Exception in thread "main" java.lang.StackOverflowError at org.ExceptionExample.<init>(ExceptionExample.java:7) at org.ExceptionExample.<init>(ExceptionExample.java:7) at org.ExceptionExample.<init>(ExceptionExample.java:7)
How to Handle Exceptions?
This section going to walk through the exception handling in Java. We all know that handling of exception is good practice for applications. But, how to do that?. Java provides keywords try
,catch
,finally
,throw
and throws
for handling the exceptions. We will go through each one in detail and how to use them in your Java program.
- Also Read : Exceptions in JSP
try and catch
The try and catch block is used for writing the code that might throw an exception. In simple words, put the code that would throw exceptions inside the try and catch block. Every try block must be followed by a catch or finally block. It is perfectly valid for a try block to ignore catch and have only a finally block. A try block may have more than one catch or finally block. There are many other rules and best practices to use the try and catch block, you would be learning those only when you are working with real applications.
- Also Read : Exception Handling in Spring Applications
Here is the example of a try and catch block:
public void method(){ try{ //statements here }catch (Exception e){ e.printStackTrace(); }
finally
Finally blocks are executed always even there is exception thrown by the applications. It is good practice to put the code which must be executed for cleaning up the resources, etc. For example : If you want to close the database connection or close the file operations, finally block is the right place to write that code.
Finally block appears after the try or catch block. The syntax for the finally block looks like this:
try { //Protected code }catch(Exception e1) { //Catch block }catch(Exception e2) { //Catch block }catch(Exception e3) { //Catch block }finally { //The finally block always executes. }
throw
throw
is used for invoking the exception explicitly. When throw statement is encountered in your program, the next statement is not executed. The control is immediatlely transferred to catch block to see if the thrown exception is handled there. If none of the catch block is handling the exception, the thrown exception will be shown to the user. We can use throw
keyword for throwing new exception or re-throwing the exception caught in the catch block.
Let’s look at the simple example for throw:
public class ExceptionExample { public static void main(String args[]) { try { new ExceptionExample().method2(); } catch (IOException e) { System.out.println("Exception handled in this method : " + e.getMessage()); } } public void method1(){ File file = new File("wrongfile.txt"); try { FileInputStream fin = new FileInputStream(file); System.out.println(fin.read()); } catch (FileNotFoundException e) { throw e; } catch (IOException e) { throw e; } } public void method2() throws IOException { method1(); } }
In the above example exceptions are re-thrown from the catch block and handled in another method. It is good practice to re-throw exceptions only when we are not handling in that particular method and handling in the caller method.
throws
throws
is used for postponing the exception handling and can be used as the alternative for try and catch block. Throws is declared at the end of method signature.If the method might throw multiple exceptions, all the exception can be declared in the method signature with coma separated.
Suppose, one method is declaring the throws
clause in the method signature, then calling method must handle those exceptions.
Here is the simple example for using throws clause:
public void method2() throws IOException { method1(); }
Using the throws clause is that we are just avoiding the try and catch but the actual handling is propagated to caller method. If you declare an exception in the throws clause, then the caller method muse handle the exception. Otherwise code will not compile.
Custom Exceptions
Custom exceptions are the exceptions that are application specific exceptions. These exceptions are not part of the Java’s built-in exception hierarchy. If you are working on a Java application, there would be a requirement to write your own custom exceptions to handle the application specific errors which will be more convenient for the application to understand and interpret the messages to the users in-case of any error.
Note that if you want to write checked exception, please extend your exception class with java.lang.Exception
or if you want to write runtime exception, then please extend your exception class with java.lang.RuntimeException
.
I have written a very simple custom exception that would explain you the real purpose for writing our own exception. Let’s look at the below code:
InSufficientFundException.java
package net.javabeat.exception; public class InSufficientFundException extends Exception { private int id; public int getId() { return id; } public void setId(int id) { this.id = id; } public InSufficientFundException() { super(); } public InSufficientFundException(String message, int id) { super(message); this.id = id; } public InSufficientFundException(String message, Throwable t) { super(message, t); } @Override public String toString() { return super.toString(); } @Override public String getMessage() { return super.getMessage() + "for account : " + id; } }
ExceptionExample.java
package net.javabeat.exception; public class ExceptionExample { public static void main(String args[]) throws InSufficientFundException { throw new InSufficientFundException( "There is not enough fund in this account ", 100); } }
When you run the above program you will get the following exception message:
Exception in thread "main" net.javabeat.exception.InSufficientFundException: There is not enough fund in this account for account : 100 at net.javabeat.exception.ExceptionExample.main(ExceptionExample.java:5)
Above example implements a custom exception InSufficientFundException
to throw error message when there is not enough fund in the bank account, probably there will be a check in the application and then this exception will be thrown. I felt it is irrelevant to write more code for account operations and that is not our goal here. We have to understand how to write a custom exception class.
When you create a custom exception instance, you can pass the message and Throwable
object to the super class exception. Also you can override the getMessage
method to customize the error message shown in the exception.
Improvements in Java 7
1. Multiple Exceptions in Catch Block:
There are lot of new features introduces as part of the Java 7 release (some more). One of them is the catching multiple exceptions in the single catch block. Prior to Java 7, a catch block can catch only one exception at a time. With the Java 7 release, developers can add multiple exceptions using the “|” symbol.
Let’s look at a simple example:
public void method1() throws IOException { File file = new File("wrongfile.txt"); try { FileInputStream fin = new FileInputStream(file); System.out.println(fin.read()); } catch (IOException | NullPointerException e) { throw e; } }
- But, you can not declare the same exception hierarchy in the same catch block. For example,
IOException
andFileNotFoundException
can not be in the same catch block since both are of the same type. You would get the error message something like :The exception FileNotFoundException is already caught by alternative exception IOException
. - Another point is that when you handle more than one exception in single catch block, the exception variable used in the catch block is implicitly
final
. You can not assign any value to the variablee
.
2. Try with Resources statement:
Another improvement in Java 7 is introduction of try-with-resource statement for auto-closing the resource instances. A resource is an object, that must be closed after it finished the operation. Any object that implements the class java.lang.AutoCloseable
or java.io.Closeable
can be used as a resource.
This classes will override the method close
that will be invoked automatically at the time of closing the operation. Prior to java 7, this method will be invoked in the finally
block. But, if there is any exception in the finally block, that also has to be handle. This handling become much easier in Java 7.
Let’s look at the example snippet:
try ( Resource myResource2 = new Resource("TRY-WITH-RESOURCE")){ myResource2.useResource(); }catch ( SomeException | IOException ex){ ex.printStackTrace(); }
If you want to read more about this feature, please our previous tutorial on try-with-resource block or official documentation.
3. Suppressed Exceptions Handling in Java 7
Another nice feature introduced in the Java 7 release is the handling of suppressed exception while using the try-with-resource block. Prior to java 7, second exception thrown while closing the resource was suppressed and not shown to the user.
With the improvements, lot of code has been reduced and Java itself handles the suppressed exception. Here is the more detailed tutorial on suppressed exceptions used in Java 7 with multiple scenarios.
Java Exception Handling Best Practices
1. Never suppress the exceptions in the catch block
2. Always catch the specific exception class in the catch block instead of just catching the java.lang.Exception
class.
Let’s look at the following sample code:
public void method(){ try{ //statements here }catch (FileNotFoundException e){ //handle here }catch (IOException e){ //handle here } }
3. Don’t throw exception from the finally block. If finally throws exception, it will be lost forever. If there is a case that finally would thrown an exception, either you handle it or log the error message. Don’t allow the exception to be un-handled.
4. Always catch the exceptions that you can handle. This is one of the important best practice to be remembered. If you look at many of the applications, there will be a catch block and there won’t be any statements for handling the exceptions. For the worst case, missing any log statements. This becomes very difficult to trace the issue if you suppress the errors.
5. Use finally blocks instead of catch blocks if you are not going to handle the exceptions.
Summary
This tutorial explained the basic idea behind writing the exceptions, different categories of exceptions, exception handling, how to write custom exceptions and finally the best practices for writing the efficient exceptions for your application. If you have any questions please write it in the comments section.