Java Tutorial 7: Exceptions

Exceptions in Java are used to deal with problems that arise during the execution of your program. They should only be used to deal with unexpected situations; when you expect a certain problem to arise under normal circumstances, you should not use exceptions but instead should write your code to check for the problem and deal with it some other way.

We speak about a method throwing an exception. The "exception" is actually a class that derives from Exception; and it is 'thrown' out of the method, stopping execution of the method, and must eventually be caught somewhere.

The Call Stack



Take a look at the following code.




public class Application {

    
    public static void main(String[] args) {
        Application application = new Application();
        
        application.run();
    }
    
    private void speak() {
        System.out.println("Hello");
    }
    
    private void run() {
        speak();
    }

}




Hello




Here speak() is called by run() which is called by main(); the list or "stack" of functions is known as the call stack. As we move from run() to main() we talk about moving "up" the call stack.

Exceptions are thrown from a method and travel up the call stack until they are either caught or else are thrown out of main (where they crash your program -- not good, so you should always catch them).

Two Types of Exceptions



There are two basic types of exceptions in Java, checked exceptions and unchecked exceptions, also known as runtime exceptions.

Both types of exception dervive from the Exception class.

Checked exceptions must be caught and dealt with in your code, or else thrown further up the call stack; this is enforced by the compiler.

Runtime exceptions do not have to be caught; by default they are thrown straight up the call stack and out of your main function. They derive from the class RuntimeException.

Runtime Exceptions



Runtime exceptions, also known as unchecked exceptions, are only thrown when something is seriously wrong with your code; your application generally won't be expected to recover from an error of this magnitude

Let's take an example of a runtime exception, division by zero.




public class Application {

    
    public static void main(String[] args) {
        Application application = new Application();
        
        application.run();
    }
    
    private void speak() {
        System.out.println("Hello");
        
        int one = 1;
        int zero = 0;
        
        int div = one/zero;
        
        System.out.println("Never reached.");
    }
    
    private void run() {
        speak();
    }

}





As soon as we tried to divide by zero, a runtime exception was thrown. Being a runtime exception, none of our code checked for it or caught it (although we could have caught it if we'd wanted to, using the techniques we'll look at shortly); it was thrown all the way out of main and crashed our program. Java then helpfully printed the stack trace, which lists the full call stack.

Hello
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at Application.speak(Application.java:18)
    at Application.run(Application.java:24)
    at Application.main(Application.java:9)




The code underneath the point where the exception occurs in the speak() method is never called.

Checked Exceptions



Your code will usually not check for runtime exceptions; they are so serious they if they occur at all, you'd better rethink your code. Checked exceptions on the other hand, are exceptions that the compiler forces you to deal with somehow.

Take a look at the following class. The Machine class has a public method called run(). This method declares that it throws an IOException. IOException (input-output exception) is part of the Java standard library. To use it, we need to add import java.io.IOException; to the top of any file that uses the exception (java.io is a package, as we'll see in a future tutorial).

Imagine that the run() method contains some code that detects an input-output problem; the method then throws the exception by using the keyword throw and creating a new IOException object.

import java.io.IOException;


public class Machine {
    public void run() throws IOException {
        
        boolean malfunction = false;
        
        System.out.println("Running ....");
        
        /*
         * Imagine that here there's some
         * code that determines the machine
         * is malfunctioning.
         */
        malfunction = true;
        
        if(malfunction) {
            throw new IOException();
        }
        
        System.out.println("Everything OK");
    }
}




Note that just because the prototype (top line) of the run() method says that it throws the IOException method, the method doesn't need to actually throw it anywhere. Of course it will only throw the exception if something goes wrong, but it may even not mention the exception --- the code would still compile, but really then it shouldn't say that it might throw the exception when it never does.

If we now try to create a new Machine object and call the run() method, we'll get a compilation error. IOException is a 'checked' exception and must either be thrown from a method or else handled.

One way of making our code compile is to throw the exception up the call stack.


import java.io.IOException;



public class Application {

    
    public static void main(String[] args) throws IOException {
        Application application = new Application();
        
        application.run();
    }
    
    private void run() throws IOException {
        
        Machine machine = new Machine();
        
        machine.run();
    }
    
}




Here, Machine has thrown IOException; our application run() method has then thrown the exception further up the call stack to main(), which then throws it out of the program altogether. In fact, our program now crashes, and when run in Eclipse or another IDE or on the command line, a stack trace is helpfully output.

Note that each method in the call stack must here declare that it throws the exception.

Running ....
Exception in thread "main" java.io.IOException
    at Machine.run(Machine.java:19)
    at Application.run(Application.java:18)
    at Application.main(Application.java:11)




Note also that we had to deal with the exception; if we had tried to call the Machine's run method without doing something about the exception, it would not have compiled; Java would tell us that we needed to deal with the exception somehow.

The above is a stupid way to deal with a checked exception, because our program will be liable to crash when something occurs that we should be able to recover from. Instead, it's better to catch the exception. We can catch the exception at any point in the call stack; any individual method in the chain between main() and the call to machine.run() can chose to either throw the exception or else to catch it.

Let's catch it as early as possible, right where we call machine.run().

import java.io.IOException;



public class Application {

    
    public static void main(String[] args) {
        Application application = new Application();
        
        application.run();
    }
    
    private void run() {
        
        Machine machine = new Machine();
        
        try {
            machine.run();
        } catch (IOException e) {
            System.out.println("Caught the exception.");
        }
        
        System.out.println("Carry on and do other stuff ...");
    }
    
}





Running ....
Caught the exception.
Carry on and do other stuff ...





In the above code, we've caught the exception with a try ... catch block.

Although the code in the Machine.run() method was terminated as soon as the exception was thrown, the rest of our program can now continue, after catching the exception. Here we've just displayed a message upon catching the exception.

The exception that was created with new and thrown in Machine.run() is basically passed to the catch clause is if it was a sort of method. To illustrate this, we can call various methods of the exception object in the catch clause.

Two particularly helpful methods are printStackTrace(), which prints the stack trace, and getMessage().

We'll look at getMessage() in a minute, but first let's change our catch clause like this:

try {
    machine.run();
} catch (IOException e) {
    e.printStackTrace();
}
        




We've called the printStackTrace() method of the IOException object.

Running ....
java.io.IOException
    at Machine.run(Machine.java:19)
    at Application.run(Application.java:19)
    at Application.main(Application.java:11)
Carry on and do other stuff ...




Attaching Messages to Exceptions



The Exception parent class defines a constructor that lets you pass a string to the exception. You can then retrieve that string using the getMessage() method.

Let's change the code in the machine class like this:

throw new IOException("Oh no -- the machine is malfunctioning");




Then we'll modify the try...catch block that catches the exception like this:

try {
    machine.run();
} catch (IOException e) {
    System.out.println(e.getMessage());
}




The program now displays this:

Running ....
Oh no -- the machine is malfunctioning
Carry on and do other stuff ...




This handy feature allows you to attach informative error messages to your exceptions. However, the end user of an application should never seen an exception message, and certainly not a stack trace, which to an end user looks like a missive from Hell itself. Always handle things nicely for the end user. Exceptions are for programmer's eyes only.

Rethrowing Exceptions



Sometimes you want to handle an exception partially somewhere in your code, but then throw it further up the stack to force other functions to take action too. In this case you can re-throw the exception from your catch block (Note: the 'e' in this code is just a variable name which the exception is passed into; it could be anything).

import java.io.IOException;



public class Application {

    
    public static void main(String[] args) {
        Application application = new Application();
        
        try {
            application.run();
        } catch (IOException e) {
            System.out.println("Dealt with finally here somehow.");
        }
    }
    
    private void run() throws IOException {
        
        Machine machine = new Machine();
        
        try {
            machine.run();
        } catch (IOException e) {
            System.out.println(e.getMessage());
            
            throw e;
        }
        
        System.out.println("Carry on and do other stuff ...");
    }
    
}




Running ....
Oh no -- the machine is malfunctioning
Dealt with finally here somehow.




Catching Multiple Exceptions



Suppose we modify the Machine class to throw multiple exceptions; we must change the throws statement, add any necessary import statements for the new exception classes, then we can throw any of the exceptions we chose.

import java.io.FileNotFoundException;
import java.io.IOException;


public class Machine {
    public void run() throws IOException, FileNotFoundException {
        
        boolean malfunction = false;
        
        System.out.println("Running ....");
        
        /*
         * Imagine that here there's some
         * code that determines the machine
         * is malfunctioning.
         */
        malfunction = true;
        
        if(malfunction) {
            throw new FileNotFoundException("Oh no -- the machine is malfunctioning");
        }
        
        System.out.println("Everything OK");
    }
}




The strange thing is, our code still executes with the Application class unchanged. Why? Because FileNotFoundException is a subclass of IOException, so a catch block that catches IOException will also catch FileNotFoundException, which is after all a kind of IOException.

We can deal with FileNotFoundException explicitly by adding multiple catches, however. We must deal with FileNotFoundException first (or else it would be caught as an IOException).

try {
    machine.run();
} 
catch(FileNotFoundException e) {
    System.out.println("File not found.");
}
catch (IOException e) {
    System.out.println(e.getMessage());
    
    throw e;
}




Running ....
File not found.
Carry on and do other stuff ...




finally



Sometimes you want to ensure that some cleanup code is always run when an exception is handled, but only if the exception is handled and not otherwise.

For instance, maybe you try to open a file; your code encounters any one of several possible exceptions, such as a FileNotFoundException. Then you want close the file after handling whichever exception it is. You can do this with a finally block. The finally code is guaranteed to be executed whenever an exception is handled.

try {
    machine.run();
} 
catch(FileNotFoundException e) {
    System.out.println("File not found.");
}
catch (IOException e) {
    System.out.println(e.getMessage());
    
    throw e;
}
finally {
    System.out.println("Here we can reset the machine or whatever.");
}




Running ....
File not found.
Here we can reset the machine or whatever.
Carry on and do other stuff ...






Not Dealing With Exceptions



You should always handle exceptions in a sensible fashion. However, sometimes you get so sick of them that you just want your code to shut up about exceptions, but your program won't compile till you handle all checked exceptions. In this case, you can simply leave the catch block empty. But don't be surprised when your code silently fails and you don't know why.

Creating Your Own Exceptions



Usually you can find some derived class of Exception that fits the bill when you want to throw an exception of your own. If none of them fit, you can always resort to just using the Exception class itself with an appropriate message. But if you're developing a serious application and you want to create a custom exception class, of course it's possible.

In the following code we extend Exception to create our own exception. We then add some extra functionality to it for our own purposes.



public class ShutterSpeedException extends Exception {
    private double shutterSpeed;
    
    public ShutterSpeedException(double shutterSpeed, String message) {
        // Call the superclass constructor to set the message.
        super(message);
        

        this.shutterSpeed = shutterSpeed;
    }
    
    public double getShutterSpeed() {
        return shutterSpeed;
    }
}





Now let's make our Machine class throw a ShutterSpeedException:


public class Machine {
    public void run() throws ShutterSpeedException {
        
        double shutterSpeed = 5.4;
        
        if(shutterSpeed > 1.0) {
            throw new ShutterSpeedException(shutterSpeed, "Shutter speed is too high.");
        }
        
    }
}





Finally, we'll create a Machine class and run it:



public class Application {

    
    public static void main(String[] args) {
        Application application = new Application();
        
        application.run();
        
        
    }
    
    public void run() {
        Machine machine = new Machine();
        
        try {
            machine.run();
        } catch (ShutterSpeedException e) {
            System.out.println(e.getMessage());
            System.out.println("Shutter speed is: " + e.getShutterSpeed());
        }
    }
    
}





The results are as follows. We've used our new ShutterSpeedException class to add extra detail to the exception.

Shutter speed is too high.
Shutter speed is: 5.4