7.4. What happens when an exception is thrown?

[8.2] Create a try-catch block and determine how exceptions alter normal program flow

[8.4] Create and invoke a method that throws an exception

In this section, we’ll uncover what happens when an exception is thrown in Java. We’ll work through several examples to understand how the normal flow of code is disrupted when an exception is thrown. We’ll also define an alternative program flow for code that may throw exceptions using try-catch-finally blocks.

As with all other Java objects, an exception is an object. All types of exceptions subclass java.lang.Throwable. When a piece of code hits an obstacle in the form of an exceptional condition, it creates an object of the class java.lang.Throwable (at runtime, an object of the most appropriate subtype is created), initializes it with the necessary information (such as its type, an optional textual description, and the offending program’s state), and hands it over to the JVM. The JVM blows a siren in the form of this exception and looks for an appropriate code block that can “handle” this exception. The JVM keeps account of all the methods that were called when it hit the offending code, so to find an appropriate exception handler it looks through all the tracked method calls.

Reexamine the class Trace and the ArrayIndexOutOfBoundsException thrown by it, as mentioned in section 7.1.3. Figure 7.11 illustrates the propagation of the exception ArrayIndexOutOfBoundsException thrown by method2 through all the methods.

Figure 7.11. Propagation of an exception through multiple method calls

To understand how an exception propagates through method calls, it’s important to understand how method calls work. An application starts its execution with the method main, and main may call other methods. When main calls another method, the called method should complete its execution before main can complete its own execution.

An operating system (OS) keeps track of the code that it needs to execute using a stack. A stack is a type of list in which the items that are added last to it are the first ones to be taken off it—last in, first out. This stack uses a stack pointer to point to the instructions that the OS should execute.

Now that you have this basic information under your belt, here’s a step-by-step discussion of exception propagation, as shown in figure 7.11:

  1. When the method main starts its execution, its instructions are pushed onto the stack.
  2. The method main calls the method method1, and instructions for method1 are pushed onto the stack.
  3. method1 calls method2; instructions for method2 are pushed onto the stack.
  4. method2 throws an exception: ArrayIndexOutOfBoundsException. Because method2 doesn’t handle this exception itself, it’s passed to the method that called it—method1.
  5. method1 doesn’t define any exception handler for ArrayIndexOutOfBounds-Exception, so it hands this exception over to its calling method—main.
  6. There are no exception handlers for ArrayIndexOutOfBoundsException in main. Because there are no further methods that handle ArrayIndexOutOfBounds-Exception, execution of the class Trace stops.

You can use try-catch-finally blocks to define code to execute when an exception is thrown, as discussed in the next section.

7.4.1. Creating try-catch-finally blocks

When you work with exception handlers, you often hear the terms try, catch, and finally. Before you start to work with these concepts, I’ll answer three simple questions:

  • Try what? First, you try to execute your code. If it doesn’t execute as planned, you handle the exceptional conditions using a catch block.
  • Catch what? You catch the exceptional event arising from the code enclosed within the try block and handle the event by defining appropriate exception handlers.
  • What does finally do? Finally, you execute a set of code, in all conditions, regardless of whether the code in the try block throws any exceptions.

Let’s compare a try-catch-finally block with a real-life example. Imagine you’re going river rafting on your vacation. Your instructor informs you that while rafting, you might fall off the raft into the river while crossing the rapids. In such a condition, you should try to use your oar or the rope thrown toward you to get back into the raft. You might also drop your oar into the river while rowing your raft. In such a condition, you should not panic and should stay seated. Whatever happens, you’re paying for this adventure sport.

Compare this to Java code:

  • You can compare river rafting to a class whose methods might throw exceptions.
  • Crossing the rapids and rowing a raft are methods that might throw exceptions.
  • Falling off the raft and dropping your oar are the exceptions.
  • The steps for getting back into the raft and not panicking are the exception handlers—code that executes when an exception arises.
  • The fact that you pay for the sport, whether you stay in the boat or not, can be compared to the finally block.

Let’s implement the previous real-life examples by defining appropriate classes and methods. To start with, here are two barebones exception classes—FallInRiver-Exception and DropOarException—that can be thrown by methods in the class RiverRafting:

class FallInRiverException extends Exception {}
class DropOarException extends Exception {}
Note

You can create an exception of your own—a custom exception—by extending the class Exception (or any of its subclasses). Although the creation of custom classes is not on this exam, you may see questions in the exam that create and use custom exceptions. Perhaps these are included because hardly any checked exceptions from the Java API are on this exam. Coding questions on the exam may create and use custom exceptions.

Following is a definition of class RiverRafting. Its methods crossRapid and rowRaft may throw exceptions of type FallInRiverException and DropOarException:

The method crossRapid at throws the exception FallInRiverException. When you call this method, you should define an exception handler for this exception. Similarly, the method rowRaft at throws the exception DropOarException. When you call this method, you should define an exception handler for this exception.

When you execute methods that may throw checked exceptions (exceptions that don’t extend the class RuntimeException), enclose the code within a try block. catch blocks that follow a try block should handle all the checked exceptions thrown by the code enclosed in the try block (checked exceptions are covered in detail in section 7.2.3).

The code shown in figure 7.12 uses the class RiverRafting as defined previously and depicts the flow of control when the code on line 3 (riverRafting.crossRapid(11);) throws an exception of type FallInRiverException.

Figure 7.12. Modified flow of control when an exception is thrown

The example in figure 7.12 shows how exceptions alter the normal program flow. If the code on line 3 throws an exception (FallInRiverException), the code on lines 4 and 5 won’t execute. In this case, control is transferred to the code block that handles FallInRiverException. Then control is transferred to the finally block. After the execution of the finally block, the code that follows the try-catch-finally block is executed. The output of the previous code is as follows:

Cross Rapid
Get back in the raft
Pay for the sport
After the try block

If you modify the previous example code as follows, no exceptions are thrown by the code on line 3 (modifications in bold):

The output of the previous code is as follows:

Cross Rapid
Row Raft
Enjoy River Rafting
Pay for the sport
After the try block

What do you think the output of the code would be if the method rowRaft threw an exception? Try it for yourself!

Exam Tip

The finally block executes regardless of whether the try block throws an exception.

Single try block, multiple catch blocks, and a finally block

For a try block, you can define multiple catch blocks but only a single finally block. Multiple catch blocks are used to handle different types of exceptions. A finally block is used to define cleanup code—code that closes and releases resources, such as file handlers and database or network connections.

When it comes to code, it makes sense to verify a concept by watching it in action. Let’s work through a simple example so that you can better understand how to use the try-catch-finally block.

In the following listing, the constructor of the class FileInputStream may throw a FileNotFoundException, and calling the method read on an object of FileInputStream, such as fis, may throw an IOException.

Listing 7.1. Code flow with multiple catch statements and a finally block

Table 7.1 compares the code output that occurs depending on whether the system is able or unable to open (and read) file.txt.

Table 7.1. Output of code in listing 7.1 when the system is unable to open file.txt and when the system is able to open file.txt but unable to read it

Output if the system is unable to open file.txt

Output if the system is able to open file.txt but unable to read it

File not found finally Next task.. File Opened File Closing Exception finally Next task..

In either of the cases described in table 7.1, the finally block executes, and after its execution, control is transferred to the statement following the try-catch block. Here’s the output of the class MultipleExceptions if none of its code throws an exception:

File Opened
Read File
finally
Next task..

It’s time now to attempt this chapter’s first Twist in the Tale exercise. When you execute the code in this exercise, you’ll understand what happens when you change the placement of the exception handlers (answers are in the appendix).

Twist in the Tale 7.1

Let’s modify the placement of the finally block in listing 7.1 and see what happens.

Given that file.txt doesn’t exist on your system, what is the output of the following code?

import java.io.*;
public class MultipleExceptions {
    public static void main(String args[]) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("file.txt");
            System.out.println("File Opened");
            fis.read();
            System.out.println("Read File");
        } finally {
            System.out.println("finally");

        } catch (FileNotFoundException fnfe) {
            System.out.println("File not found");
        } catch (IOException ioe) {
            System.out.println("File Closing Exception");
        }
        System.out.println("Next task..");
    }
}

  1. The code prints
    File not found
    finally
    Next task..
  2. The code prints
    File Opened
    File Closing Exception
    finally
    Next task..
  3. The code prints File not found.
  4. The code fails to compile.

7.4.2. Using a method that throws a checked exception

To use a method that throws a checked exception, you must follow the handle-or-declare rule (section 7.3.2). In the following code, the method main in the class TestRiverRafting won’t compile because it doesn’t handle or declare the checked exception FallInRiverException declared to be thrown by the method crossRapid:

The main method in the classes Handle, Declare, and HandleAndDeclare compiles successfully because they follow the handle-or-declare rule:

class Handle {
    public static void main(String args[]) {
        RiverRafting riverRafting = new RiverRafting();
        try {
            riverRafting.crossRapid(9);
        } catch (FallInRiverException e) {
            System.out.println("Exception : " + e);
        }
    }
}
class Declare {
    public static void main(String args[]) throws FallInRiverException {
        RiverRafting riverRafting = new RiverRafting();
        riverRafting.crossRapid(9);
    }
}
class HandleAndDeclare {
    public static void main(String args[]) throws FallInRiverException {
        RiverRafting riverRafting = new RiverRafting();
        try {
            riverRafting.crossRapid(9);
        } catch (FallInRiverException e) {
            System.out.println("Exception : " + e);
        }
    }
}
Exam Tip

To use a method that throws a checked exception, you must follow the handle-or-declare rule.

7.4.3. Using a method that throws a runtime exception

If a method throws a runtime exception, the exception name isn’t required to be included in the method’s declaration (though it is allowed). To use a method that throws a runtime exception, you don’t need to follow the declare-or-handle rule. Here’s an example:

Here’s another example. Examine the following code, which throws a runtime exception (ArrayIndexOutOfBoundsException):

The preceding code doesn’t print output from System.out.println("All seems to be well"). The code execution halts with the exception thrown by the code that tries to output the value of students[5].

It’s possible to create an exception handler for the exception ArrayIndexOutOfBoundsException thrown by the previous example code, as follows:

public class InvalidArrayAccess {
    public static void main(String args[]) {
        String[] students = {"Shreya", "Joseph"};
        try {
            System.out.println(students[5]);
        } catch (ArrayIndexOutOfBoundsException e){
            System.out.println("Exception");
        }
        System.out.println("All seems to be well");
    }
}

The output of the previous code is as follows:

Exception
All seems to be well

In the same way you can catch a checked exception, you can also catch a Runtime-Exception. On real projects, the preferred approach is to avoid runtime exceptions by including appropriate checks. For example, in the previous code, you can prevent ArrayIndexOutOfBoundsException from being thrown by using appropriate checks:

7.4.4. Using a method that throws an error

Errors are serious exceptions thrown by the JVM, such as when it runs out of stack memory or can’t find the definition of a class. You shouldn’t define code to handle errors. You should instead let the JVM handle the errors.

In the remainder of this section, we’ll look at some frequently asked questions on try-catch-finally blocks that often overwhelm certification aspirants.

7.4.5. Will a finally block execute even if the catch block defines a return statement?

Imagine the following scenario: a guy promises to buy diamonds for his girlfriend and treat her to coffee. The girl inquires about what will happen if he meets with an exceptional condition during the diamond purchase, such as inadequate funds. To the girl’s disappointment, the boy replies that he’ll still treat her to coffee.

You can compare the try block to the purchase of diamonds and the finally block to the coffee treat. The girl gets the coffee treat regardless of whether the boy successfully purchases the diamonds. Figure 7.13 shows this conversation.

Figure 7.13. A little humor to help you remember that a finally block executes regardless of whether an exception is thrown

It’s interesting to note that a finally block will execute even if the code in the try block or any of the catch blocks defines a return statement. Examine the code in figure 7.14 and its output, and note when the class ReturnFromCatchBlock is unable to open file.txt.

Figure 7.14. The finally block executes even if an exception handler defines a return statement.

As you can see from figure 7.14’s code output, the flow of control doesn’t return to the method main when the return statement executes in the catch handler of FileNotFoundException. It continues with the execution of the finally block before control is transferred back to the main method. Note that control isn’t transferred to the println statement "Next task.. " that follows the try block because the return statement is encountered in the catch block, as mentioned previously.

Going back to the example of the guy and his girlfriend, a few tragic conditions, such as an earthquake or tornado, can cancel the coffee treat. Similarly, there are a few scenarios in Java in which a finally block does not execute:

  • Application termination—The try or the catch block executes System.exit, which immediately terminates the application.
  • Fatal errors— A crash of the JVM or the OS occurs.

In the exam, you may be questioned on the correct order of two or more exception handlers. Does order matter? See for yourself in section 7.4.9.

7.4.6. What happens if both a catch and a finally block define return statements?

In the previous section, you saw that the finally block executes even if a catch block defines a return statement. For a method that defines a try-catch-finally block, what is returned to the calling method if both catch and finally return a value?

Here’s an example:

The output of the preceding code is

20

If both the catch and finally blocks define return statements, the calling method will receive a value from the finally block.

7.4.7. What happens if a finally block modifies the value returned from a catch block?

If a catch block returns a primitive data type, the finally block can’t modify the value being returned by it. Here’s an example:

The output of the preceding code is as follows:

About to return :10
Return value is now :20
In Main:10

Even though the finally block adds 10 to the variable returnVal, this modified value is not returned to the method main. Control in the catch block copies the value of returnVal to be returned before it executes the finally block, so the returned value is not modified when finally executes.

Will the preceding code behave in a similar manner if the method returns an object? See for yourself:

This is the output of the preceding code:

About to return :10
Return value is now :1010
In Main:1010

In this case, the catch block returns an object of the class StringBuilder. When the finally block executes, it can access the value of the object referred to by the variable returnVal and can modify it. The modified value is returned to the method main. Remember that primitives are passed by value and objects are passed by reference.

Exam Tip

Watch out for code that returns a value from the catch block and modifies it in the finally block. If a catch block returns a primitive data type, the finally block can’t modify the value being returned by it. If a catch block returns an object, the finally block can modify the state of the object being returned by it.

7.4.8. Can a try block be followed only by a finally block?

Syntactically, you can define a try block that might only be followed by a finally block:

class NoCatchOnlyFinally {
    public static void main(String args[]) {
        String name = null;
        try {
            System.out.println("Try block : open resource 1");
            System.out.println("Try block : open resource 2");

            System.out.println("in try : " + name.length());
            System.out.println("Try block : close resources");
        } finally {
            System.out.println("finally : close resources");
        }
    }
}

Here’s the output of the preceding code:

Try block : open resource 1
Try block : open resource 2
finally : close resources
Exception in thread "main" java.lang.NullPointerException
    at NoCatchOnlyFinally.main(NoCatchOnlyFinally.java:7)

Because main() in the preceding code throws an unchecked exception, NullPointer-Exception, it compiles successfully. But if the code enclosed within a try block declares to throw a checked exception, either it must be followed by a catch block, or the method in which it is defined must declare to throw it.

7.4.9. Does the order of the exceptions caught in the catch blocks matter?

Order doesn’t matter for unrelated classes. But it does matter for related classes sharing an IS-A relationship because catch blocks are checked from the top down to find a suitable one to handle a given exception.

In the latter case, if you try to catch an exception of the base class before an exception of the derived class, your code will fail to compile. This behavior may seem bizarre, but there’s a valid reason for it. As you know, an object of a derived class can be assigned to a variable of a base class. Similarly, if you try to catch an exception of a base class before its derived class, the exception handler for the derived class can never be reached, so the code will fail to compile.

Examine the code in figure 7.15, which has been modified by defining the catch block for IOException before the catch block for FileNotFoundException.

Figure 7.15. The order of placement of exception handlers is important.

Figure 7.16 depicts an interesting way to remember that order matters. As you know, a thrown exception looks for an appropriate exception handler, starting with the first handler and working toward the last. Let’s compare a thrown exception to a tiger and the exception handlers to doors that allow certain types of creatures to enter. Like a thrown exception, the tiger should start with the first door and move on to the rest of the doors until a match is found.

Figure 7.16. A visual way to remember that the order matters for exceptions caught in the catch blocks

The tiger starts with the first door, which allows all animals to enter. Voilà! The tiger enters the first door and never reaches the second door, which is meant specifically for tigers. In Java, when such a condition arises, the Java compiler refuses to compile the code because the later exception handler code will never execute. Java doesn’t compile code if it contains unreachable statements.

Rules to remember

Here are a few more rules you’ll need to answer the questions in the OCA Java SE 8 Programmer I exam:

  • A try block may be followed by multiple catch blocks, and the catch blocks may be followed by a single finally block.
  • A try block may be followed by either a catch or a finally block or both. But a finally block alone won’t suffice if code in the try block throws a checked exception. In this case, you need to catch the checked exception or declare it to be thrown by your method. Otherwise, your code won’t compile.
  • The try, catch, and finally blocks can’t exist independently.
  • The finally block can’t appear before a catch block.
  • A finally block always executes, regardless of whether the code throws an exception.

7.4.10. Can I rethrow an exception or the error I catch?

You can do whatever you want with an exception. Rethrow it, pass it on to a method, assign it to another variable, upload it to a server, send it in an SMS, and so on. Examine the following code:

Oops! The previous code fails to compile, and you get the following compilation error message:

ReThrowException.java:9: unreported exception java.io.FileNotFoundException; must be caught or declared to be thrown
            throw fnfe;
            ^

When you rethrow a checked exception, it’s treated like a regular thrown checked exception, meaning that all the rules of handling a checked exception apply to it. In the previous example, the code neither caught the rethrown FileNotFoundException exception nor declared that the method myMethod would throw it using the throw clause. Hence, the code failed to compile.

The following (modified) code declares that the method myMethod throws a FileNotFoundException, and it compiles successfully:

Another interesting point to note is that the previous code doesn’t apply to a Runtime-Exception. You can rethrow a runtime exception, but you’re not required to catch it, nor must you modify your method signature to include the throws clause. The simple reason for this rule is that RuntimeExceptions aren’t checked exceptions, and they may not be caught or declared to be thrown by your code (exception categories are discussed in detail in section 7.2).

7.4.11. Can I declare my methods to throw a checked exception instead of handling it?

If a method doesn’t wish to handle the checked exceptions thrown by a method it calls, it can declare to throw these exceptions using the throws clause in its own method declaration. Examine the following example, in which the method myMethod doesn’t include an exception handler; instead, it rethrows the IOException thrown by a constructor of the class FileInputStream using the throws clause in its declaration:

Any method that calls myMethod must now either catch the exception IOException or declare that it will be rethrown in its method signature.

7.4.12. I can create nested loops, so can I create nested try-catch blocks too?

The simple answer is yes, you can define a try-catch-finally block within another try-catch-finally block. Theoretically, the levels of nesting for the try-catch-finally blocks have no limits.

In the following example, another set of try-catch blocks is defined in the try and finally blocks of the outer try block:

Now comes another Twist in the Tale exercise that’ll test your understanding of the exceptions thrown and caught by nested try-catch blocks. In this one, an inner try block defines code that throws a NullPointerException. But the inner try block doesn’t define an exception handler for this exception. Will the outer try block catch this exception? See for yourself (answer in the appendix).

Twist in the Tale 7.2

Given that players.txt exists on your system and that the assignment of players, shown in bold, doesn’t throw any exceptions, what’s the output of the following code?

import java.io.*;
public class TwistInTaleNestedTryCatch {
    static FileInputStream players, coach;
    public static void main(String args[]) {
        try {
            players = new FileInputStream("players.txt");
            System.out.println("players.txt found");
            try {
                coach.close();
            } catch (IOException e) {
                System.out.println("coach.txt not found");
            }
        } catch (FileNotFoundException fnfe) {
            System.out.println("players.txt not found");
        } catch (NullPointerException ne) {
            System.out.println("NullPointerException");
        }
    }
}

  1. The code prints
    players.txt found
    NullPointerException
  2. The code prints
    players.txt found
    coach.txt not found
  3. The code throws a runtime exception.
  4. The code fails to compile.

7.4.13. Should I handle errors?

Although you can define code to handle errors, you shouldn’t. You should instead let the JVM handle the errors. The following example shows how it’s possible to catch an error:

Though you shouldn’t handle errors in your code, what happens if you do? Will the exception handler that handles the code execute? See for yourself by answering the question in the following Twist in the Tale exercise (answer in the appendix).

Twist in the Tale 7.3

Will the code in the error-handling block execute? What do you think is the output of the following code?

public class TwistInTaleCatchError {
    public static void main(String args[]) {
        try {
            myMethod();
        } catch (StackOverflowError s) {
            for (int i=0; i<2; ++i)
                System.out.println(i);
        }
    }
    public static void myMethod() {
        myMethod();
    }
}

  1. 0
  2. java.lang.StackOverFlowError
  3. 0
    1
  4. 0
    1
    2
    java.lang.StackOverFlowError

In the next section, you’ll work with specific exception classes and errors that are on the exam.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.129.26.108