You may have encountered compilation errors several times in your code. Compilation errors are errors that are detected when the code is compiled and even before the code is executed. However, some errors are detected and can occur only when the code is run. Such errors are broadly classified into Exceptions.

In this article, I will be covering what Exceptions are and how Java supports Exception handling.

  1. What is an Exception
  2. How Exception Handling Works
  3. Exception Handling in Code
  4. Throws and Throw
  5. Java’s Built-In Exceptions

Java Exception Handling

What is an Exception

Simply speaking, an exception represents an abnormal condition that halts the normal flow of execution. Java provides an elaborate exception handling mechanism that allows the code to recover from such error conditions with minimal impact on the application.

How Exception Handling Works

Java has an in-built exception class called java.lang.Throwable. It has several sub-classes corresponding to specific errors like a file access error, divide by 0 error, null error, etc. As soon as an abnormal condition occurs, the Java runtime environment creates an Exception object of the appropriate sub-class and “throws” it.

The runtime environment tries to find some exception handler code that can process the exception. The handler “catches the exception”. The exception handler can have code to either recover from the exception if possible or log the error and proceed with the rest of the code. Java has a default handler which catches exceptions not caught by the code. The default handler displays a message about the exception and terminates the program.

The following code demonstrates this:

String s = null; //1
int length = s.length(); //2
System.out.println("The length of s is "+length); //3
System.out.println("Doing something else"); //4
  • Line 1 declares a String variable s and assigns it a null
  • Line 2 attempts to print the length of s.
  • This causes the Java runtime system to create an object of NullPointerException and throw it
  • The default handler catches the exception and prints an error message
  • As soon as the exception occurs at Line 2, program execution halts, so lines 3-4 are not executed

So, this code prints the following output:

Exception in thread "main" java.lang.NullPointerException
	at demo.ExceptionHandlingDemo.main(ExceptionHandlingDemo.java:8)

Exception Handling in Code

Try/Catch

Java provides the try and catch keywords for explicit exception handling in code. This gives the code a chance to recover from an exception. You need to put code that you want to monitor for exceptions in a try block.

After the try block, you need to specify a catch block. A catch block is an exception handler and handles a particular type of exception. So within the catch block, you need to write the code that you want to be executed when that particular exception occurs.

The above code can be rewritten using try/catch as follows:

try { //1
	String s = null; //2
	int length = s.length();  //3
	System.out.println("The length of s is " + length);  //4
} catch (NullPointerException e) { //5
	System.out.println("String s is null"); //6
} //7
System.out.println("Doing something else"); //8
  • Line 1 specifies a try statement
  • Lines 2-4 attempt to print the length of the String s
  • Lines 5-7 specify a catch block that handles a NullPointerException
  • Like before, Line 3 throws a NullPointerException
  • This causes Line 4 to be skipped.
  • Java begins to look for a catch block that can handle the exception.
  • It finds the catch block at Line 5 and executes the code at Line 6
  • Once the catch block completes, it executes Line 8 and proceeds with the rest of the code.

So, this code prints the following output:

String s is null
Doing something else

So, the try/catch block allows the code to print a user-friendly error message and then proceed with the rest of the code.

Finally

Sometimes, there may be some important code that needs to be executed irrespective of whether an exception occurs or not. You need to place such code in a finally block. The following code demonstrates this:

try { //1
	String s = null; //2
	int length = s.length(); //3
	System.out.println("The length of s is " + length); //4
			
} catch (NullPointerException e) { //5
		System.out.println("String s is null"); //6
	} //7
	finally { //8
		System.out.println("Code in finally"); //9
	} //10
  • Lines 2-4 print the length of the String s
  • Lines 5-7 specify a catch block that handles a NullPointerException
  • Lines 8-10 specify a finally block that prints some text
  • Line 3 throws a NullPointerException which causes the code in the catch block at Line 6 to be executed
  • Once the catch block completes, the code in the finally block at Line 9 is executed

So, this code prints the following output:

String s is null
Code in finally

Even if the exception had not occurred, the code within the finally block would still have been executed.

Throws and Throw

There are two additional keywords related to exception handling called throw and throws. Although both look and sound similar, they work very differently. Let us understand how both these keywords work.

Throws

The throws keyword is used to specify that a method throws an exception but does not handle it, it leaves it up to the caller of the method to handle the exception. In such a case, the throws keyword can be specified in the method declaration along with the name of the exception that the method throws. If a method throws a checked exception, then it must either be caught and handled by the method or declared in the throws clause, otherwise, a compilation error occurs. Unchecked exceptions need not be specified in the throws clause. Checked and unchecked exceptions are covered in the next section.

Consider the following code:

public class LoadClass {
 public void loadClass(String className) throws ClassNotFoundException {
  Class.forName(className);
 }
}
  • This code specifies a class called LoadClass
  • Line 2 specifies a method called loadClass which accepts a parameter a class name and loads it in memory via the Class.forName
  • The Class.forName can cause a ClassNotFoundException. Since the loadClass method does not handle this exception via a try/catch, it is specified in the throws clause
  • So, the method declaration is followed by the throws keyword, followed by the ClassNotFoundException. This indicates that the loadClass method can throw a ClassNotFoundException, so the callers of this method need to handle this exception via a try/catch statement.

Now, consider the following code that invokes the loadClass method:

public class Main {
 public static void main(String[] args) {
  LoadClass lc = new LoadClass();
  try {
   lc.loadClass("com.MissingClass");
  } catch (ClassNotFoundException e) {
     System.out.println("This class is not present:"+e.getMessage());
  }
 }
}
  • This code specifies a class called Main
  • Line 6 invokes loadClass via a LoadClass object called lc
  • Line 6 needs to be enclosed in a try/catch block that handles the ClassNotFoundException since the loadClass method has specified a ClassNotFoundException in a throws clause. If a try/catch block is not specified, a compilation error occurs, unless the exception is specified in a throws clause in the main method declaration

So, this code prints the following output:

This class is not present:com.MissingClass

Throw

The throw keyword can be used to explicitly throw an exception. This is generally used for user-defined exceptions but can be used for in-built exceptions as well. The throw keyword needs to be followed by an exception object.

Consider the following code:

public class PhoneNumberValidator {
 public boolean validate(long phoneNumber) {
  String strNumber = String.valueOf(phoneNumber);
  if(strNumber.length() == 10 ) {
   return true;
  }
  else {
   throw new RuntimeException("Invalid Phone Number");
  }
 }
}
  • This code specifies a class called PhoneNumberValidator
  • Line 2 specifies a method called validate. It accepts a parameter called phoneNumber and returns a boolean value that specifies whether it is a valid phone number or not
  • So, if the input number is not a valid phone number (has length less than 10), then the else block at Line 8 throws a RuntimeException
  • Line 9 uses a throw statement to throw the exception. So, the throw keyword is followed by a new object of type RuntimeException with an appropriate error message.
  • Any code that invokes this method can now catch and handle this exception in a try/catch block.

Java’s Built-In Exceptions

Java provides a rich set of built-in Exceptions that can handle most programming errors. The java.lang.Throwable is the superclass of all the exception classes. It has two main sub-classes as follows:

Error

The Error class defines exceptions that occur due to serious errors related to the Runtime Environment and are not due to your code. These do not occur under normal circumstances and your code is not expected to catch and handle them. Some examples are OutOfMemoryError, VirtualMachineError, StackOverflowError, etc

Exception

The Exception class is used for error conditions that might occur in your code like division by 0, error while reading a file, etc. Exceptions are of two types as follows:

Unchecked Exceptions – These are exceptions that are not checked by the compiler. So even if you write code that can throw an unchecked exception, a compilation error does not occur. Some examples are NullPointerException, ArrayIndexOutOfBoundsException, IllegalArgumentException, etc

Checked Exceptions – These are exceptions that are checked by the compiler. So if you write code that might throw a checked exception, a compilation error occurs which warns you to handle the exception. Some examples are SQLException, IOException, etc

Conclusion

So, in this article, we saw what an Exception is and how Java’s exception handling mechanism works. We also understood the try/catch/finally keywords and touched upon Java’s in-built exceptions.