1. Exceptions

>

At long last, programmers thought to standardize and automate error handling. This happened when exceptions were invented. Now the exception mechanism handles 80% of exceptional situations.

If some scholar came up with exceptions, it was likely the subject of his or her doctoral dissertation. If a programmer came up with it, then he may have received a friendly pat on the back from a coworker: "Seems okay, bro."

When an error occurs in a Java program, such as division by 0, some wonderful things happen:

Step one

A special exception object is created, which contains information about the error that occurred.

Everything in Java is an object, and exceptions are no exceptions 🙂 Exception objects have their own classes, and the only thing that distinguishes them from ordinary classes is that they inherit the Throwable class.

Step two

The exception object is "thrown". Perhaps the wording here could be better. "Throwing an exception" is more like triggering a fire alarm or sounding a "DEFCON 1" alert.

When an exception is thrown to the Java machine, the normal operation of the program stops and "emergency protocols" begin.

Step three

The method in which the exception was thrown exits immediately. The exception is passed to the calling method, which also exits immediately. And so on down the chain until the main method exits. When the main method terminates, so does the program.

Example:

Code Console output
class Solution
{
   public static void main(String[] args)
   {
      System.out.println("Your attention, please! Preparing for the end of the world");
      endTheWorld();
      System.out.println("The world ended successfully");
   }

   public static void endTheWorld()
   {
      System.out.println("We're doing something important");
      doSomeWork(0);
      System.out.println("Everything is working well");
   }

   public static void doSomeWork(int n)
   {
      System.out.println("Nothing terrible will happen: " + n);
      System.out.println(2 / n);
      System.out.println("Nothing terrible happened: " + n);
   }
}
Your attention, please! Preparing for the end of the world
We're doing something important
Nothing terrible will happen: 0

An exception occurs on line 20: division by 0. The Java machine immediately creates an exception — an ArithmeticException object and "throws" it to the method.

The divide() method ends immediately, so we never see the string: Nothing terrible happened: 0. The program returns to the endTheWorld() method, and the situation repeats itself: there is an unhandled exception in the system, which means that the endTheWorld() method also terminates abnormally. Then the main method terminates, and the program stops.

What is the purpose of these exceptions? Well, you can write your own code to catch particular types of exceptions and write your own logic to handle exceptional situations.


2. Catching exceptions: try-catch

Java has an exception catching mechanism that lets you halt this abnormal termination of methods. It looks like this:

try
{
   // Code where an exception might occur
}
catch(ExceptionType name)
{
   // Exception handling code
}

This construct is called a try-catch block.

Code where exceptions may occur is wrapped in curly braces, preceded by the word try.

After the curly braces, we have the catch keyword and, inside parentheses, the declaration of an exception variable. This is followed by curly braces that wrap the code to be executed if an exception of the specified type occurs.

If no exceptions are thrown during execution of the "primary code", then the code inside the catch block will not be executed. If an exception occurs, then it will be (if the type of the thrown exception is the same as the type of the variable in the parentheses).

Example:

Code Console output
class Solution
{
   public static void main(String[] args)
   {
      System.out.println("Hadron Collider launched");

      try
      {
         launchHadronCollider(1);
         launchHadronCollider(0);
      }
      catch(Exception e)
      {
         System.out.println("Error! Caught an exception");
         System.out.println("The planet was sucked into a black hole!");
      }

      System.out.println("The Hadron Collider stopped");
   }

   public static void launchHadronCollider(int n)
   {
      System.out.println("Everything is working well: " + n);
      System.out.println(2/n);
      System.out.println("There are no problems: " + n);
   }
}
Hadron Collider launched
Everything is working fine: 1
2
There are no problems: 1
Everything is working fine: 0
Error! Caught an exception
The planet has been sucked into a black hole!
The Hadron Collider is stopped


3. Multiple catch blocks

Multiple catch blocks

In theory, all sorts of exceptions can be thrown in a block of code. Some you will want to handle one way, others another way, and still others you will decide not to handle at all.

Java developers decided to help you out and let you write not one but many catch blocks after the try block.

try
{
   // Code where an exception might occur
}
catch (ExceptionType1 name1)
{
   // Code for handling ExceptionType1
}
catch (ExceptionType2 name2)
{
   // Code for handling ExceptionType2
}
   catch (ExceptionType3 name3)
{
   // Code for handling ExceptionType3
}

Example:

Code Console output
class Solution
{
   public static void main(String[] args)
   {
      System.out.println("Start of main method");
      try
      {
         calculate(0);
      }
      catch (ArithmeticException e)
      {
         System.out.println("Division by 0");
      }
      catch(Exception e)
      {
         System.out.println("Caught some kind of exception");
      }

      System.out.println("End of main method");
   }

   public static void calculate(int n)
   {
      System.out.println("Start of calculate method: " + n);
      System.out.println(2/n);
      System.out.println("End of calculate method: " + n);
   }
}
Start of main method
Start of calculate method: 0
Division by 0
End of main method


4. Order of catch blocks

Exceptions that occur in a try block can only be caught by a single catch block. You cannot have an exception handling situation where the code from multiple catch blocks gets executed.

But the order of the blocks matters.

You can have a situation where an exception could be caught by multiple blocks. If that is the case, then the exception will be caught by whichever catch block comes first (closest to the try block).

How can you have a situation where multiple catch blocks can catch the same exception?

All exceptions belong to a single inheritance hierarchy — see the diagram.

Exception hierarchy

An ArithmeticException object can be assigned to a variable whose type is ArithmeticException or any of its ancestor classes: RuntimeException, Exception and Throwable — see the diagram.

We'll talk more about inheritance and ancestor classes in Level 21.

This code will compile just fine:

Benefits of inheritance:
ArithmeticException ae    = new ArithmeticException();
RuntimeException runtime  = new ArithmeticException();
Exception exception       = new ArithmeticException();
Throwable trwbl           = new ArithmeticException();

So you can catch an ArithmeticException with any of the 4 catch blocks above.

Example 1:

Code Console output
class Solution
{
   public static void main(String[] args)
   {
      System.out.println("Start of main method");
      try
      {
         calculate(0);
      }
      catch(ArithmeticException e)
      {
         System.out.println("Division by 0");
      }
      catch(Exception e)
      {
         System.out.println("Caught some kind of exception");
      }

      System.out.println("End of main method");
   }

   public static void calculate(int n)
   {
      System.out.println("Start of calculate method: " + n);
      System.out.println(2/n);
      System.out.println("End of calculate method: " + n);
   }
}
Start of main method
Start of calculate method: 0
Division by 0
End of main method

In this example, the ArithmeticException can be caught by both the catch (Exception e) and catch (ArithmeticException e) blocks. It will be caught by the block closest to the try block — the first catch block.

To avoid surprises, it is best to place catch blocks that can catch nearly every exception near the end of the list of catch blocks.

The Throwable type is generally capable of catching every possible exception in Java. If you put it in the first catch block, then the code won't compile, since the compiler knows that there are unreachable blocks of code.