In this section, we will learn what the try catch statements are and how they work in Java.
Note: Before reading this section, we’re assuming you already read the exception handling section (In that section, we gave a general view of what exceptions and errors are and how they occur in a program).
Try Catch Blocks in Java
The `try` and `catch` blocks are used to control part of the instructions of a program that might throw one or multiple exceptions and also handle these exceptions. So the program can continue running without crashing.
We used the word `exception` multiple times. But the question is: what really exception is in Java?
What is exception in Java?
Exceptions are objects of type built-in classes (You’ll soon see some of them in this section). When at runtime, a problem (exception) occurs, the runtime engine will automatically create an object from a class that is related to that particular exception and sends it back instead of proceeding to running the rest of instructions in the program. This object that represents the exception should be handled properly via a `catch` block, otherwise the program will crash.
An exception object has multiple methods that we can use to find out the reason of the exception, the place where the exception occurred, and some other types of information about the exception.
How to declare Try Catch Block? (Try Block syntax)
Alright, now let’s see the structure of `try and catch` blocks:
try { // Instructions that might throw exceptions } catch(Exception e) { // Instructions to handle exceptions }
A `try` and `catch` blocks come after each other. That means we first declare the body of a `try` block and then put the `catch` block afterwards.
`try{ }` block: We use the `try{}` block to put instructions that might throw an exception. After a `try` block, there should be at least one `catch` or `finally` block.
`catch( ){ }` block: we use `catch(){}` block to handle the exception that might be thrown from a `try` block.
Note: you’ll soon see what the parentheses after the `catch` keyword does.
How Does Try Catch Block Work in Java?
Usually, in a `try` block multiple exceptions might be thrown. For this reason, after a `try` block, we can use multiple `catch` blocks and each will be in charge of handling one specific exception.
For example let’s say we have a program that opens a file and reads its content.
A couple of exceptions that might occur in this program are:
- The target file that a user set its address in the program might not exist! For this reason, the program will return an object of type FileNotFoundException class. As mentioned at the beginning of this section, the object represents the exception which will be sent automatically by the runtime engine.
- The program might try to write some content in the target file, but the file is read only! So when the program attempts to do that, we will get another object of type ReadOnlyException class that again represents an exception.
Now back to the pair of parentheses that come after the `catch` keyword:
In the parentheses, we put the class type of the exception (object) that this catch block can handle. You can think of a `catch` as a method and the pair of parentheses as the place where we put the parameter of the `catch` block. For example, if we put a parameter of type `ReadOnlyException` class, it means this catch block only accept exceptions (objects) of type `ReadOnlyException` class.
After that, the runtime engine will check every `catch` block after the `try` block and match the type of exception (object) with the type of parameter that we set for each `catch` block.
The runtime engine will check each of these `catch` blocks and the first one that matched will be executed.
Java Catch Block Notes:
- The type of parameter that we set in a `catch` block should be either of type the `Throwable` class or a child class that inherits from this class. This is because the `Throwable` class is considered as the superclass of all other exception types.
For example, two types of exceptions (objects) that might be thrown in a program can both be `IndexOutOfBoundsException` and `NullPointException`. Both of these classes inherit from `Throwable` class.
Example:
try{ }catch(String ex ){ }
This will return error because the `String` type is not a subclass of the `Throwable` class.
But the example below is correct because the `IOException` is in fact a subclass of the `Throwable` class:
try{ }catch(IOException ex ){ }
In inheritance section, we mentioned that a parent class variable (parameter in this case) can store reference to objects of its type and objects of children types. So because the `Throwable` class is the base class for all other Exception types (subclasses) we can create only one `catch` block and set its parameter to be of type `Throwable`. In such case, the target catch-block is able to catch any type of exception that might be thrown at runtime.
Example: exception handling via try catch block
public class Simple { public static void main(String[] args) { try{ System.out.println(3/0); }catch (ArithmeticException e) { System.out.println("The body of the catch block: "); System.out.println(e.getMessage()); e.printStackTrace(); } System.out.println("Done!"); } }
Output:
The body of the catch block: / by zero java.lang.ArithmeticException: / by zero at tuto.Simple.main(Simple.java:7) Done!
Exception handling and control flow in Java
Here’s the flow of control when an exception occurs in a `try` block:
First, the Java runtime creates an object of the appropriate class to represent the exception that has occurred. The first catch block following the try block is checked. If the exception object can be assigned to the parameter for the catch block, the parameter of the catch block is assigned the reference of the exception object, and the control is transferred to the body of the catch block. When the catch block finishes executing its body, the control is transferred to the point following the try-catch block. It is very important to note that after executing the catch block the control is not transferred back to the try block. Rather, it is transferred to the code that follows the try-catch block. If a try block has many catch blocks associated with it, a maximum of one catch block is executed.
So in the example above, within the body of the `try` block we’ve divided the value 3 by 0 and because there’s no result of this division, the runtime engine created an exception (object) of type `ArithmeticException` and throw it.
Now the `catch` block that comes after the `try` block is checked and here as you can see because the type of the parameter of this `catch` block is the same as the type of exception that is thrown, then the control is passed to the body of this block.
Java Throwable Class
We mentioned that all exceptions in Java inherit from `Throwable` class. Inside this class, there are a couple of methods that are useful to get the reason behind the exception and the place where the exception occurred.
So here two of the methods of this class that we used are: `getMessage()` and `printStackTrace()`.
`getMessage()`: this method will return a `String` value in which it explains the reason of the exception.
`printStackTrace()`: calling this method will print the details about where the exception occurred. This information is: the class name and the method name and the line number where the exception occurred.
Java Exception Hierarchy
In Java, exception classes are in a hierarchy where one exception class is the parent of another class and also is the subclass or another one, and so on.
Take a look at the picture below:
As you can see, the `Exception` class is the subclass of the `Throwable` class but also the superclass of the `IOException` and `RuntimeException` classes. Also these two classes are themselves the super classes of other child classes.
The pattern in the exception hierarchy is that on top of the tree we have more general classes and as we go down the tree, the exception classes become more and more specific.
For example, on top of the tree, we can see the `Exception` class. But under this class there are two subclasses in which one of them `IOException` is related to exceptions that occur when working with files (opening, reading, writing etc.) and another one `RuntimeException` relates to those exceptions that occur because of problems that might happen in Java Virtual Machine (JVM).
So as you can see, the lower we get, the more specific the type of exceptions will be.
The point here is that, if we create a catch block that has a parameter of type super class (A general one); it will also catch the exceptions that are of type subclasses (those that are more specific). For this reason, if you have two or more `catch` blocks after a `try` block, make sure you put those that are more specific on the first catch blocks and for the next catch blocks, use those that are more general.
In short:
The first catch block should handle the most specific exception type and the last the most generic catch exception type.
Example: handling file related exceptions in Java
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class Simple { public static void main(String[] args) { try{ FileInputStream fileInputStream = new FileInputStream("adfahdfjka"); int i = fileInputStream.read(); }catch (FileNotFoundException e) { System.out.println("The body of the FileNotFoundException block: "); System.out.println(e.getMessage()); e.printStackTrace(); }catch (IOException io){ System.out.println("The body of the IOException"); System.out.println(io.getMessage()); } System.out.println("Done!"); } }
Output:
The body of the FileNotFoundException block:
adfahdfjka (The system cannot find the file specified)
java.io.FileNotFoundException: adfahdfjka (The system cannot find the file specified)
at java.base/java.io.FileInputStream.open0(Native Method)
at java.base/java.io.FileInputStream.open(FileInputStream.java:219)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157)
at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112)
at tuto.Simple.main(Simple.java:11)
Done!
The body of the FileNotFoundException block: adfahdfjka (The system cannot find the file specified) java.io.FileNotFoundException: adfahdfjka (The system cannot find the file specified) at java.base/java.io.FileInputStream.open0(Native Method) at java.base/java.io.FileInputStream.open(FileInputStream.java:219) at java.base/java.io.FileInputStream.<init>(FileInputStream.java:157) at java.base/java.io.FileInputStream.<init>(FileInputStream.java:112) at tuto.Simple.main(Simple.java:11) Done!
Here we’ve used the `FileInputStream` class to open a file and read its content.
Note: the `FileInputStream` class is explained in details later in this tutorial.
When opening a file, the typical exceptions that might be thrown are:
- FileNotFoundException: this type of exception is specific and occurs when we’re trying to open a file that does not exist.
This class is a subclass of the IOException.
- IOException: This is more general one (it’s the superclass of the `FileNotFoundException`).
In this example, we used it to handle other types of exceptions that might occur while working on a file.
The point of this example was that if we change the location of the `IOException` catch block and put it as the first `catch` block, it will handle both `FileNotFoundException` type of exception and other exceptions related to files.
In such case, this would make the `FileNotFoundException` catch block an obsolete.
Note: As a matter of fact, we will get a compile time error if we try to change the location of these two catch blocks!
Example: Java File Exception Handling
import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class Simple { public static void main(String[] args) { try{ FileInputStream fileInputStream = new FileInputStream("adfahdfjka"); int i = fileInputStream.read(); }catch (IOException e) { }catch (FileNotFoundException io){ } System.out.println("Done!"); } }
Output:
Error:(16, 10) java: exception java.io.FileNotFoundException has already been caught
Java Catch Block with Multiple Exceptions:
In the parentheses of a `catch` block we can set multiple types of exceptions each separated via vertical bar `|` and so that catch block will handle any type of exception declared as its parameter.
Example: catch block with multiple exceptions
import java.io.FileInputStream; import java.io.IOException; public class Simple { public static void main(String[] args) { try{ FileInputStream fileInputStream = new FileInputStream("adfahdfjka"); int i = fileInputStream.read(); System.out.println(4/0); }catch (IOException | ArithmeticException exception) { System.out.println(exception.getMessage()); } System.out.println("Done!"); } }
Output:
adfahdfjka (The system cannot find the file specified) Done!
In the example above, there are two types of exceptions that might occur.
- IOException. This is related to the File processing.
- ArithmeticException: This type of exception occurs when we’re trying to divide a value by 0.
As you can see, we only used one `catch` block to handle these two exceptions.
In such case, the type of the first exception that is thrown in the program will be checked against this `catch` block. If the type of that exception matched against these two types, then the reference of the exception will be passed to the `catch` block and we can work with it.
Java Catch Block and The Compiler
The Java compiler is very peculiar about exceptions being handled by programmers. If the code in a try block cannot throw a checked exception and its associated catch blocks catch a checked exception that the try block would never throw, the compiler will generate an error.
Exception handling example:
import java.io.FileNotFoundException; public class Simple { public static void main(String[] args) { try { System.out.println("Hello"); } catch (FileNotFoundException e) { System.out.println("Exception occurred and here's the reason: "+ e.getMessage()); } } }
Output:
Error:(10, 11) java: exception java.io.FileNotFoundException is never thrown in body of corresponding try statement
In this example, the program is trying to catch and handle exceptions of type `IOException` class or any of its subclasses which occur if something wrong happens while is working on a file. But In this program we didn’t even remotely come close to opening a file or anything like it! For this reason the compiler will return error, which essentially is saying:
“Your program not in a million years, will face an exception that is remotely related to IOException class or any of its subclasses!!! So be a good developer and remove this handler from your program. “