Checked Exceptions: Catching Problems
AAHC

Click on A to make all fonts on the page smaller.

Click on A to make all fonts on the page larger.

Click on HC to toggle high contrast mode. When you move your mouse over some bold words in high contrast mode, related words are automatically highlighted. Text is shown in black and white.


Degrading Gracefully

In this lesson, we'll focus on checked exceptions. A well-written application will anticipate and provide mechanisms for recovery from these exceptional conditions. We want you to understand exactly what's happening when an Exception is thrown. We'll illustrate that process so you'll be confident using, and ultimately defining your own checked exceptions.

I/O Exceptions

Other than RuntimeException and its subclasses, the most common exceptions occur when attempting to access files that are either nonexistent or inaccessible. The next course will discuss Java's classes for input and output (I/O) in more explciit detail, but for now, we'll use an less complicated example that reads a file to demonstrate the use of checked exceptions in the java.io package.

Create a new java4_Lesson7 project. If you're given the option to Open Associated Perspective, click No. In this new project, create a FileFetcher class as shown:

Type FileFetcher as shown in blue:

CODE TO TYPE: FileFetcher
package exceptionTesting;

import java.io.FileReader;
import java.io.BufferedReader;

public class FileFetcher {
    String aLine="";                 // we will look at the file one line at a time

    public void getHomework() {
        FileReader myFile = new FileReader("homework.txt");  // create a Reader for a file--we will define this file next
        BufferedReader in = new BufferedReader(myFile);      // wrap the FileReader in a class that lets us manipulate it
        aLine = in.readLine();                               // read in a line of the file
        System.out.println(aLine);                           // print it to the Console
    } 

    public static void main(String [] args){
        FileFetcher testMe = new FileFetcher();
        testMe.getHomework();
    }
}  

We see two sources of errors:

Save and run it anyway. Even though Java complains about Errors, click Proceed.

We have Unresolved compilation problems. (Also, since our program could not compile, it threw a java.lang.Error):

On the first line of code with an error, we invoke the FileReader constructor, so let's go to that part of the API to find out more.

Go to the java.io package. Scroll down to the FileReader class in the Class Summary. Look at its constructor FileReader(File file):

In the Editor, on the next line with an error, we are calling the in.readLine() method. in is an instance of BufferedReader, so let's look at its readLine() method.

Go back to the java.io package. Scroll down to the BufferedReader class in the Class Summary. Look at its readLine() method. Sure enough, there's our exception:

Exception Types

We have seen various types of exceptions. Not every catch clause will work for every exception that's thrown.

NoteAn exception handler is considered appropriate if the type of the exception object thrown matches the type that it can handle.

Using Try and Catch

Since these are checked exceptions, Java will not let us compile the code to run it until we handle them. In this example, we aren't really handling the situation (in that we are not addressing the "big picture" by, for example, finding replacement files), but we are handling the individual exceptions the code throws, by providing try/catch clauses for them. In a real application, you would do something more in these catch blocks to recover from the exception in a more meaningful way.

Edit FileFetcher as shown in blue below:

CODE TO EDIT: FileFetcher
package exceptionTesting;

import java.io.FileReader;
import java.io.BufferedReader;

public class FileFetcher {
    String aLine="";        // we will look at the file one line at a time

    public void getHomework() {
        try 
        {
            FileReader myFile = new FileReader("homework.txt");  // create a Reader for a file - we will define this file next
            System.out.println("I did get here");
            in = new BufferedReader(myFile);                     // wrap the FileReader in a class that lets us manipulate it
        }
        catch (FileNotFoundException e)
        {
            System.out.println("Can't find the file, but keep going anyway--allows for future problems!");
        }
        try
        {             
            aLine = in.readLine();                               // read a line of the file
        }
        catch(IOException e){
            System.out.println("Now we have some other problem!");
        }
        System.out.println(aLine);                               // print it to the Console
    } 

    public static void main(String [] args){
        FileFetcher testMe = new FileFetcher();
        testMe.getHomework();
    }
}

That didn't help much:

We can fix these. The unresolved issues can be resolved by importing appropriate classes (java.io.FileNotFoundException and java.io.IOException). The variable in cannot be resolved due to a scope issue. It is declared in one block of code, a try clause, and then used in another try clause.

Edit FileFetcher. Add the imports and declare the instance of FileReader and BufferedReader so they can be seen by all methods:

CODE TO EDIT: FileFetcher
package exceptionTesting;

import java.io.FileReader;        // could also import java.io.*; to get all of these
import java.io.BufferedReader;
import java.io.FileNotFoundException;   
import java.io.IOException;

public class FileFetcher {
    String aLine="";
    FileReader myFile;      // declare FileReader and BufferedReader as instance variables
    BufferedReader in;                                                           

    public void getHomework() {
        try 
        {
            myFile = new FileReader("homework.txt");  // Do NOT declare here too or scope stays within try block
            System.out.println("I did get here");
            in = new BufferedReader(myFile);         // Do NOT declare here too or scope stays within try block
        }
        catch (FileNotFoundException e)
        {
            System.out.println("Can't find the file, but keep going anyway - allows for future problems!");
        }
        try
        {             
            aLine = in.readLine();                          // read a line of the file
        }
        catch(IOException e){
            System.out.println("Now we have some other problem!");
        }
        System.out.println(aLine);                    // print it to the Console
    } 
   
    public static void main(String [] args){
        FileFetcher testMe = new FileFetcher();
        testMe.getHomework();
    }
}

All of the errors are gone now.

Save and run it.

Of course, we can't find a file--we haven't made one yet. Let's make one now. This time we're making a new File, not a Java Class. Right-click on the java4_Lesson7 Project folder. Select New | File. Enter the name homework.txt and click Finish.

Now the file structure for your java4_Lesson7 looks like this:

Type homework.txt as shown:

CODE TO TYPE: homework.txt
I do not like doing my homework
so I will try to make my parents do it.

I will tell them that I am sick and see
if I can get away with it.

Save it and run the FileFetcher class. Your code has this in the Console now:

Forwarding the Exception

We've already used Java methods that throw and handle exceptions . Now we'll forward the exceptions for other methods to handle.

To see the entire text file in your console output, edit FileFetcher as shown:

CODE TO EDIT: FileFetcher
package exceptionTesting;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FileReader;
import java.io.BufferedReader;

public class FileFetcher {
    FileReader myFile;
    BufferedReader in;
    String aLine =""; 

    public void getHomework() {
        try 
        {
            myFile = new FileReader("homework.txt");  // create a Reader for a file
            System.out.println("I did get here");
            in = new BufferedReader(myFile);        // wrap the FileReader in a class that lets us manipulate it
        }
        catch (FileNotFoundException e){
            System.out.println("Can't find the file, but keep going anyway--allows for future problems!");
        }
        
        while (aLine != null){
            try {
                aLine = in.readLine();
            } 
            catch(IOException e){
                System.out.println("Now we have some other I/O problem");
            }
            if (aLine !=null) System.out.println(aLine);  // we had another readLine after the check for null
        }  
        // later, we will do something more here
    }

    public static void main(String [] args){
        FileFetcher testMe = new FileFetcher();
        testMe.getHomework();
    }
}

Save and run it.

Determining What To Do With Exceptions
Throwing Exceptions

Java methods may throw exceptions at runtime. Sometimes these errors can be avoided through well-written code, but others are unavoidable because we can't always anticipate what a user will do until runtime. Similarly, in handling checked exceptions, a method may know that it has an exceptional condition, but the not know how to handle it. The method would need to throw the exception, so the user of the method could specify how to handle the problem given the current environment.

All code in an object-oriented program (like Java) is performed through the use of methods, and methods that call other methods. So exceptions will always be thrown in (or more accurately, by) methods to some other method that invoked it.

In fact, this is precisely the reason it is throwing the exception. If a method (say method1()) can handle an exceptional condition, it should. Only when a method doesn't know what to do with an exception, does it need to throw it to the method (say method2()) that is using method1() with the potentially exceptional condition. Keep in mind that if method1() is throwing, then method2() needs to catch--assuming method2 is in an environment where it knows what to do.

If method2() is not in such an environment, then it needs to specify that it will throw (forward) the exception as well. Then the method that called it (say method3()) would need to catch it.

This is the call stack:

The throws could be passed back down the stack like this:

...and still be caught:

The Java runtime system searches the call stack for a method that contains a block of code that can handle the exception.

Example

We will let our FileFetcher class represent a student who is supposed to be writing a file named homework.txt. Our student wants to convince his parents that he has too much to do and cannot do his homework this time around; he is not going to "handle" the homework situation. He is not going to handle the exceptions for retrieving his homework file in the FileFetcher method of getHomework(). Here are the conditions that represent Method2 for us:

  1. The Method(s) where the error occurred throw the exceptions. Constructor FileReader("homework.txt") and readLine() will be Method1(a) and Method1(b), respectively.
  2. The FileFetcher method of getHomework() is Method2: Method without an exception handler.

So, after all that work we did to handle the problem within the method itself, now our student says he is going to forward the exceptions. We'll see.

Edit FileFetcher as shown in blue and red:

CODE TO EDIT: FileFetcher
package exceptionTesting;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FileReader;
import java.io.BufferedReader;

public class FileFetcher {
    FileReader myFile;
    BufferedReader in;
    String aLine =""; 

    public void getHomework() throws FileNotFoundException, IOException {

        // try {
            myFile = new FileReader("homework.txt");
            System.out.println("I did get here");
            in = new BufferedReader(myFile); 
        // } 
        // catch (FileNotFoundException e){
        //     System.out.println("Can't find the file, but keep going anyway - allows for future problems");
        // }
        while (aLine != null){
            // try {
                aLine = in.readLine();
            // } 
            // catch(IOException e){
                // System.out.println("Now we have some other I/O problem");
            // }
            if (aLine !=null) System.out.println(aLine);             // we had another readLine after the check for null 
        } 
    }

    public static void main(String [] args){
        FileFetcher testMe = new FileFetcher();
        testMe.getHomework();
    }
}  

The only active pieces of code in the getHomework() method now are the FileReader instance, the println statements, and the while loop for reading and printing lines from the homework file.

We might as well comment out the main() method too. As you can see by the error message, if the getHomework() method doesn't handle the exceptions, then the instance testMe cannot call the method, unless it handles them.

It looks like our irresponsible student isn't going to handle anything. Instead he's letting his Mom do it. Comment out the entire main method here so there are no errors:

CODE TO EDIT: FileFetcher
package exceptionTesting;
        
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FileReader;
import java.io.BufferedReader;
        
public class FileFetcher {
    FileReader myFile;
    BufferedReader in;
    String aLine =""; 
        
    public void getHomework() throws FileNotFoundException, IOException {
        // try {
            myFile = new FileReader("homework.txt");
            System.out.println("I did get here");
            in = new BufferedReader(myFile); 
        // } 
        // catch (FileNotFoundException e){
        //     System.out.println("Can't find the file, but keep going anyway - allows for future problems");
        // }
        while (aLine != null){
        //     try {
                aLine = in.readLine();
        //     } 
        //     catch(IOException e){
        //         System.out.println("Now we have some other I/O problem");
        //     }
            if (aLine !=null) System.out.println(aLine);             // we had another readLine after the check for null 
        } 
    }
        
    //public static void main(String [] args){
    //    FileFetcher testMe = new FileFetcher();
    //    testMe.getHomework();
    //}
}  

If a method in a class does not catch the exceptions from method calls within itself, then that method needs to throw those uncaught exceptions or it will not compile. Throwing exceptions rather than handling them is not the preferred way to write methods. Only do it when it's absolutely necessary. If your method can handle the exceptions from the methods that it uses, it should. Throwing methods should happen only when the application cannot deal with the exception in its current method environment.


Passing Exceptions to Other Methods

Now, let's suppose that our student's Mom says she will help, but only with a single exception (she chooses to handle FileNotFoundException), and that Dad has to help with the other.

In the java4_Lesson7 project, create a new Mom class as shown:

Type the Mom class, as shown in blue below:

CODE TO TYPE: Mom
package exceptionTesting;

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

public class Mom {
	
    public void getToDoHomework() throws IOException {
        FileFetcher testMe = new FileFetcher();
        try{
            testMe.getHomework();	
        }
        catch(FileNotFoundException e){
            System.out.println("Mom caught the File Not Found Exception.");
        }
    }

    public static void main(String [] args) throws IOException {   
        // Note: This is VERY BAD programming. Do not throw exceptions in main methods.
        Mom parent1 = new Mom();
        parent1.getToDoHomework();
    }
}  

Save and run it. You might get a warning that there are still errors--click Proceed anyway.

We are throwing an exception in this main() method to demonstrate that it's a terrible thing to do, because no one can catch from main(). That's the reason Eclipse warned that you still had errors. However, the code does run. No exception was thrown, so there was nothing for the file name to catch.

Go back to the FileFetcher class and change the file name as shown:

CODE TO EDIT: FileFetcher
package exceptionTesting;
   
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FileReader;
import java.io.BufferedReader;
   
public class FileFetcher {
    FileReader myFile;
    BufferedReader in;
    String aLine =""; 
   
    public void getHomework() throws FileNotFoundException, IOException {
    //try {
        myFile = new FileReader("homework2.txt");
        System.out.println("I did get here");
        in = new BufferedReader(myFile); 
    //} 
    //catch (FileNotFoundException e){
    //    System.out.println("Can't find the file, but keep going anyway - allows for future problems");
    //}
    while (aLine != null){
    //    try {
            aLine = in.readLine();
    //    } 
    //    catch(IOException e){
    //        System.out.println("Now we have some other IO problem");
    //    }
        if (aLine !=null) System.out.println(aLine);   // we had another readLine after the check for null 
        } 
    }
   
    //public static void main(String [] args){
    //    FileFetcher testMe = new FileFetcher();
    //    testMe.getHomework();
    //}
}  

Save it. You haven't created a homework2.txt file, so you probably have a little problem.

Open the Mom class and run it. We can see that Mom caught the FileNotFoundException; the Console shows the println from the catch clause block. Now in FileFetcher, change the filename back to homework.txt.

Catching Exceptions from Other Methods

In java4_Lesson7, create a new Dad class as shown:

Type the Dad class as shown in blue below:

CODE TO TYPE: Dad
package exceptionTesting;

import java.io.IOException;

public class Dad {
	
    public void parentalCollaboration() {
        Mom spouse = new Mom();
        try{
            spouse.getToDoHomework();
        }
        catch(IOException e){
            System.out.println("Dad caught the I/O Exception.");
        } 
    }
	
    public static void main(String [] args)  {
        Dad parent2 = new Dad();
        parent2.parentalCollaboration();
    }
}

Save and run it. You do not get any errors, because now you've caught all Exceptions.

One More Time

The last example demonstrates the way you can either handle exceptions within your code with try/catch clauses, or have your method throw the exception and let the methods' users handle them.

And in our example that passed an exception down the call stack, we saw this:

Compare that to our method with a handler:

Exception Hierarchy

Let's have Mom handle a different exception. Edit the Mom class as shown in blue:

CODE TO EDIT: Mom
package exceptionTesting;

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

public class Mom {
	
    public void getToDoHomework() throws FileNotFoundException {
        FileFetcher testMe = new FileFetcher();
        try{
            testMe.getHomework();	
        }
        catch(IOException e){
            System.out.println("Mom caught the I/O Exception.");
        }
    }

    public static void main(String [] args) throws FileNotFoundException {     
        Mom parent1 = new Mom();
        parent1.getToDoHomework();
    }
}  

And edit the Dad class as shown in blue below:

CODE TO EDIT: Dad
package exceptionTesting;

import java.io.FileNotFoundException;

public class Dad {
	
   public void parentalCollaboration() {
      Mom spouse = new Mom();
      try{
          spouse.getToDoHomework();
      }
      catch(FileNotFoundException e){
          System.out.println("Dad caught the File Not Found Exception.");
      } 
   }
	
   public static void main(String [] args)  {
      Dad parent2 = new Dad();
      parent2.parentalCollaboration();
   }
}

Go to FileFetcher and change the filename to homework2.txt.

Save all three classes: FileFetcher, Mom, and Dad.

Run from the Dad class.

Even though this was a FileNotFoundException, Mom caught it--we see the output from her catch in the Console. Why? Because she was supposed to catch the IOExceptions and Dad was supposed to catch the FileNotFoundExceptions.

Go to the java.io package. Scroll down to FileNotFoundException in the Exception Summary and take a look at its hierarchy:

Exceptions also pay attention to inheritance. Mom said she would catch IOExceptions, and FileNotFoundException inherits from IOExceptions, so it actually is an IOException. If you want to make sure to catch the right exceptions, always catch the more specific one first. Exceptions are often the results of I/O problems.

Using Finally in a Try/Catch Block

A given method can throw more than one exception. In fact, the getHomework() method in the FileFetcher class threw two exceptions. If you had wanted the Mom class to catch and handle both specifically, the method getToDoHomework() might have been written like this:

Multiple Catches
    public void getToDoHomework(){
        FileFetcher testMe = new FileFetcher();
        try { //Begin "try" block
            testMe.getHomework();	
        }
        catch(FileNotFoundException e){
            System.out.println("Mom caught the File Not Found Exception.");
        }
        catch(IOException e) {
            System.out.println("Mom caught the I/O Exception.");
        }
        finally
        {
            System.out.println("After you finish reading the file, you need to close the file streams.");
            try{
                if (testMe.in != null) testMe.in.close(); 
                    if (testMe.myFile != null) testMe.myFile.close();
            }
            catch(IOException e){}
        } // End "try" block
    }

The Mom class also added a finally clause, to remind Dad that he should close his files and input streams when he finishes reading them. The finally clause always executes when the try block exits. This guarantees that the finally block is executed even if an unexpected exception occurs. We want to allow a programmer to "clean up" after exceptions have occurred because a call from a try clause may have jumped out of code prematurely. The finally block is a key tool for preventing resource leaks.

Additional Information

There is plenty more to see in the Java Tutorial Lesson on Exceptions. Check it out. See you in the next lesson...