Resolves uncaught exceptions in Java and the nested use of try statements

  • 2020-04-01 04:13:02
  • OfStack

Java exceptions that are not caught
Before you learn to handle exceptions in your program, it's good to see what happens if you don't handle them. The following applet includes an expression that intentionally causes an error to be divided by zero.


class Exc0 {
  public static void main(String args[]) {
    int d = 0;
    int a = 42 / d;
  }
}

When the Java runtime checks that it is divided by zero, it constructs a new exception object and throws it. This causes the execution of excel 0 to stop, because once an exception is thrown, it must be caught by an exception handler and processed immediately. In this case, we didn't provide any of our own exception handlers, so the exception was caught by the default handler of the Java runtime system. Any exceptions that are not caught by your program will eventually be handled by the default handler. The default handler displays a string describing the exception, prints the stack trace where the exception occurred, and terminates the program.

Here is the output from the execution of the program by the standard javaJDK runtime interpreter:


  java.lang.ArithmeticException: / by zero
  at Exc0.main(Exc0.java:4)


Notice how the class name Exc0, method name main, file name exc0.java, and line number 4 are included in a simple stack usage trace. Also, notice that the Exception type thrown is a subclass of Exception called ArithmeticException, which more specifically describes what type of error method is thrown. As discussed later in this chapter, Java provides several built-in exception types that match the different kinds of runtime errors that can occur.

The stack trace displays the sequence of method calls that led to the error. For example, here is another version of the previous program that introduces the same error, but the error is generated in a method other than the main() method:


class Exc1 {
  static void subroutine() {
    int d = 0;
    int a = 10 / d;
  }
  public static void main(String args[]) {
    Exc1.subroutine();
  }
}

The result of the stack trace of the default exception handler shows how the entire call stack is displayed:


  java.lang.ArithmeticException: / by zero
  at Exc1.subroutine(Exc1.java:4)
  at Exc1.main(Exc1.java:7)


As you can see, the bottom of the stack is line 7 of main, which calls the subroutine() method. This method causes an exception at line 4. The call stack is important for debugging because it identifies the exact steps that led to the error.

Nesting of Java try statements
Try statements can be nested. That is, one try statement can be inside another try block. Each time a try statement is entered, the context of the exception is pushed onto the stack. If an internal try statement does not contain a catch handler with a special exception, the stack pops up and the catch handler of the next try statement checks to see if it matches. This process continues until a catch statement matches successfully, or until all nested try statements are checked. If no catch statement matches, the Java runtime system handles the exception. Here is an example of using nested try statements:


// An example of nested try statements.
class NestTry {
  public static void main(String args[]) {
    try {
      int a = args.length;
      
      int b = 42 / a;
      System.out.println("a = " + a);
      try { // nested try block
        
        if(a==1) a = a/(a-a); // division by zero
        
        if(a==2) {
          int c[] = { 1 };
          c[42] = 99; // generate an out-of-bounds exception
        }
      } catch(ArrayIndexOutOfBoundsException e) {
        System.out.println("Array index out-of-bounds: " + e);
      }
    } catch(ArithmeticException e) {
      System.out.println("Divide by 0: " + e);
    }
  }
}

As you can see, the program nested a try block within a try block. The program works like this: when you execute the program without command-line arguments, the outer try block will generate an exception that is divided by zero. The program executes with one command-line argument, and the nested try block generates an error that is divided by zero. Because the inner block does not match the exception, it passes the exception to the outer try block, where the exception is handled. If you execute the program with two command-line arguments, the inner try block generates an array-bound exception. The following results illustrate each case:


C:>java NestTry
Divide by 0: java.lang.ArithmeticException: / by zero
C:>java NestTry One
a = 1
Divide by 0: java.lang.ArithmeticException: / by zero
C:>java NestTry One Two
a = 2
Array index out-of-bounds: java.lang.ArrayIndexOutOfBoundsException

When a method is called, the nesting of try statements can occur in a very subtle way. For example, you can place calls to methods in a try block. Inside the method, there is another try statement. In this case, the try inside the method is still nested inside the try block that calls the method externally. The following is a modification of the previous example, in which the nested try block is moved inside the method nesttry() :



class MethNestTry {
  static void nesttry(int a) {
    try { // nested try block
      
      if(a==1) a = a/(a-a); // division by zero
      
      if(a==2) {
        int c[] = { 1 };
        c[42] = 99; // generate an out-of-bounds exception
      }
    } catch(ArrayIndexOutOfBoundsException e) {
      System.out.println("Array index out-of-bounds: " + e);
    }
  }

  public static void main(String args[]) {
    try {
      int a = args.length;
      
      int b = 42 / a;
      System.out.println("a = " + a);
      nesttry(a);
    } catch(ArithmeticException e) {
      System.out.println("Divide by 0: " + e);
    }
  }
}

The output of this program is the same as in the previous example.


Related articles: