© Vaskaran Sarcar 2020
V. SarcarInteractive Object-Oriented Programming in Javahttps://doi.org/10.1007/978-1-4842-5404-2_10

10. Managing Exceptions

Vaskaran Sarcar1 
(1)
Bangalore, Karnataka, India
 

When you write code for your application, you expect that it will execute without any problems. So, it is important to manage and detect all possible errors in your program. Even so, sometimes you may encounter surprises during program execution. These surprises may occur for various reasons, such as some careless mistake in the program, implementation of incorrect logic, loopholes in the code paths of the program, and so on. However, it is also true that many of the failures are beyond the control of a programmer. Programmers often term these unwanted situations as exceptions . Handling these exceptions is essential when you write any application.

Types of Mistakes

Normally, you can broadly classify the mistakes (or errors) in a program into two categories, as follows:
  • Compile-time error

  • Runtime error

Compile-time errors are detected by the Java compiler and are easy to detect. The Java compiler acts as your friend to figure out the error details. For example, it may point out a line number (where the error is encountered) and display a brief description of the error. Once you correct it, you need to recompile it to check whether the updated program is ready for execution. Sometimes, you may need to fix multiple errors, and, as a result, multiple recompilations may be needed. The Java compiler will not generate the class file if it finds these kinds of errors in your program.

Let’s consider some typographical errors in the following program:
package java2e.chapter10;
class IncorrectClass //{
       void SampleMethod() {
             System.out.println("Semicolon missing") //error
       }
}
Here, the Java compiler will display error messages. In Eclipse, you can see the error description and the corresponding line numbers, as shown in Figure 10-1.
../images/433474_2_En_10_Chapter/433474_2_En_10_Fig1_HTML.jpg
Figure 10-1

Two typographical errors in Eclipse IDE

So, you can see that you missed the bracket { after the class definition and you forgot to put a semicolon at the end of line number 5.

This type of error is common but sometimes hard to find with human eyes at the very beginning. So, the Java compiler will rescue you in these situations.

On the other hand, runtime errors are challenging. In this case, you get a green signal from the compiler, and on successful compilation you get the .class file, but your program still produces wrong results and may terminate prematurely. Here are some examples of runtime errors that can be caused by the following operations:
  • Dividing an integer by 0

  • Trying to access an array element that is out of bounds of an array

  • Using a null object to invoke a method

  • Trying to do invalid conversions, such as trying to convert an invalid string to an integer

Intentionally in some places, I am using the term mistake instead of error. The reason will be revealed to you shortly.

Definition of Exception

You can define an exception as an event that breaks the normal execution or the instruction flow of the program.

When exceptional situations arise, an exception object is created and thrown into the method that created the exception. That method may or may not handle the exception. If it cannot handle the exception, it will pass the responsibility to another method. (Similar to our daily life, when a situation goes beyond our control, we seek advice from others.) If there is no method to take responsibility for handling a particular exception, an error dialog box appears (indicating an unhandled exception), and the execution of the program stops.

Points To Remember

An exception-handling mechanism deals with runtime errors, and if they are not handled properly, an application will produce unwanted output, and it may die prematurely. Therefore, you should try to write applications that can detect and handle surprises in a graceful manner and prevent the premature death of the application.

Demonstration 1

Let’s begin with a simple example. The following program compiles successfully, but it will raise an exception during runtime because in this program, the divisor (b) becomes 0 prior to the division operation. The application produces a runtime error because, in this case, you are trying to divide 100 by 0.
package java2e.chapter10;
class Demonstration1 {
       public static void main(String[] args) {
             System.out.println("***Demonstration-1.Exploring Exceptions.***");
             int a = 100, b = 2, result;
             b -= 2;//b beomes 0
             result = a / b;
             System.out.println("The result of a/b is :" + result);
       }
}
Output:
***Demonstration-1.Exploring Exceptions.***
Exception in thread "main" java.lang.ArithmeticException: / by zero
at java2e.chapter10.Demonstration1.main(Demonstration1.java:9)

Key Points of the Exception-handling Mechanism

Before you proceed further, I’ll highlight some key points of the exception-handling mechanism. You may need to come back to these points repeatedly. It is suggested that once you finish this chapter, you come back here to review your understanding of exception handling in Java.
  • An exceptional object is created when a runtime error occurs. So, a Java exception is basically an object to describe an erroneous situation.

  • Any method in an application can raise surprises during the application’s runtime. If such a situation occurs, in programming terminology, you say that the method has thrown an exception.

  • You use the following keywords to deal with Java exceptions: try, catch, throw, throws, and finally.

  • You try to guard against an exception with a try-catch block . The code that may throw an exception is placed inside a try block, and this exceptional situation is handled inside a catch block. But if there is no exception raised in the try block, the catch blocks are bypassed completely.

  • You can associate multiple catch blocks with a try block. When a particular catch block handles the sudden surprise (the exception), you say that the catch block has caught the exception.

  • The code in the finally block must execute. A finally block is generally placed after a try block or a try-catch block. This block is used to perform some housekeeping so that the application can be gracefully closed. For example, if a file is already opened, you should close it here, or if you already allocated some resources, those should be released inside this block.

  • When an exception is raised inside a try block, the control jumps to the respective catch or finally block. The remaining part of the try block will not execute.

  • Exceptions follow the inheritance hierarchy. So, it is important to remember the hierarchy, shown in Figure 10-2.
    ../images/433474_2_En_10_Chapter/433474_2_En_10_Fig2_HTML.jpg
    Figure 10-2

    Exception hierarchy

  • You can see that both the Exception class and the Error class are subclasses of the Throwable class, which in turn derives from Object (in the java.lang package). By other exceptions, I mean classes like IOException (already defined in Java), our own custom exception classes (not defined in Java), and so on. So, you can simply say that in Java, the Throwable class is the ultimate super class of all errors and exceptions.

  • Exceptions are broadly categorized into two types: checked and unchecked . The runtime exception classes (RuntimeException class and its subclasses) and error classes (Error class and its subclasses) fall into the category of unchecked exceptions, and the others remaining are called checked exceptions. You’ll see a detailed discussion of each category shortly, and Q&A 10.8 will summarize the details.

  • This chapter primarily focuses on runtime exceptions. Errors, in general, are caused by some catastrophic failures, like JVM being out of memory, stack overflow, and so on. You can hardly do anything with them. The Java runtime environment itself needs to take care of these severe situations.

  • When you create your custom exception classes, in general, you’ll subclass from the Exception class. But it is not a rule. So, in Demonstration 7, you’ll see that a custom exception class extends from the Throwable class, and in Demonstration 8, you will notice that a custom exception class inherits from the RuntimeException class.

  • You should order the catch blocks from most specific to most general. Otherwise, you will encounter compile-time errors. For example, suppose you have placed a catch block (say, catch block1) that can handle a parent-class exception before a catch block (say catch block2) that can handle only the derived-class exception. From the compiler’s point of view, it is an example of unreachable code, because in this case catch block1 is always capable of handling the exceptions that catch block2 can handle. Therefore, control does not need to reach catch block2 at all. You will examine this scenario in an upcoming example.

  • You can use any of these combinations: try-catch, try-catch-finally, or try-finally.

  • The Java runtime system can generate exceptions. At the same time, you can also create your own exception class and throw your own exception.

  • If you do not handle exceptions, a default handler of the Java runtime system will handle it on your behalf, and the program may die prematurely.

Points To Remember

  • In Java, the Throwable class is the ultimate super class of all errors and exceptions.

  • In an exception-handling mechanism, there is a key difference between Java and C#. There is no concept of the throws keyword in C#. This is a hot topic of debate.

Demonstration 2

Now, let’s see how you can handle the exception that was encountered in the previous example.
package java2e.chapter10;
public class Demonstration2 {
       public static void main(String[] args) {
             System.out.println("***Demonstration-2.Exploring Exceptions-Demonstration1 is modified.***");
             int a = 100, b = 2, result;
             b -= 2;// b beomes 0
             try {
                   result = a / b;
                   System.out.println(" So, the result of a/b is : " + result);
             } catch (Exception ex) {
                   System.out.println("Encountered an exception " + ex.getMessage());
                   System.out.print("Here is the stack trace:");
                   ex.printStackTrace();
             } finally {
                   System.out.println("I am in finally. You cannot skip me!");
             }
       }
}
Output:
***Demonstration-2.Exploring Exceptions-Demonstration1 is modified.***
Encountered an exception / by zero
Here is the stack trace:java.lang.ArithmeticException: / by zero
I am in finally. You cannot skip me!
at java2e.chapter10.Demonstration2.main(Demonstration2.java:10)
You can confirm the following points from the output of the program:
  • When an exception was raised inside a try block, the control jumped to the respective catch block. The remaining part of the try block did not execute. (Notice that you are not seeing the line "So, the result of a/b is :" in the output.)

  • The code in the finally block executed even though the program encountered an exception. (Notice the line "I am in finally. You cannot skip me!" in the output).

  • To get the details of the exception, some built-in methods are already defined in the java.lang.Throwable class. The getMessage(), printStackTrace(), getCause(), etc. are some common examples in this category. I have used two of them—getMessage() and printStackTrace()—in this demonstration. For your immediate reference, I am just picking a sample source code snapshot to get details of the getMessage() method from Eclipse IDE, which is shown in Figure 10-3.

../images/433474_2_En_10_Chapter/433474_2_En_10_Fig3_HTML.jpg
Figure 10-3

A sample source code snapshot from Eclipse IDE

Q&A Session

10.1 I could easily put an if block like if(b==0) before the division operation to avoid a 0 divisor, and in that case I could easily exclude the use of the try-catch block. Is this understanding correct?

You are considering only this simple example, which is why it appears to you this way. Yes, in this case, you can guard your code using your proposed method. However, think of a case where the value of b is also computed at runtime (for example, you may pick a random value from a specified range, and you cannot predict the value earlier). Also, if you need to put guards like this in all probable cases, your code may look clumsy and difficult to read. But if you like a defensive programming style, you may keep asking for a valid input. So, at the end, it’s your choice as to how you want to design your software.

Demonstration 3

Now, consider the following example, which examines how to handle multiple type exceptions with multiple catch blocks. In the following program, the value of the integer b can be 0, 1, or 2. This value is generated at random. So, you cannot predict the value. Based on the generated value, you may encounter different types of exceptions. To understand it better, you may need to visit the output and analysis section multiple times.
package java2e.chapter10;
import java.util.Random;
public class Demonstration3 {
       public static void main(String[] args) {
             System.out.println("***Demonstration-3.Handling multiple Exceptions***");
             int a = 5;
             Random randomGenerator = new Random();
             // Will generate 0 to 2.
             int b = randomGenerator.nextInt(3);
             System.out.println("Current value of b is : " + b);
             int c = 0;
             try {
                   / Case-1:if b=0,it will raise ArithmeticException*/
                   c = a / b;
                   System.out.println("c=" + c);
                   int[] arr = new int[2];
                   arr[0] = 0;
                   arr[1] = c + 1;
                   if (b % 2 == 0) {
                          /* Case-2: (b is not zero here) it will raise ArrayIndexOutOfBoundsException*/
                          arr[2] = c + 2;
                   } else {
                          Object myObject = null;
                          // case-3: It raises NullPointerException
                          int hashcode = myObject.hashCode();
                   }
             } catch (ArithmeticException ex) {
                   System.out.println("Caught the ArithmeticException :" + ex.getMessage());
                   ex.printStackTrace();
             } catch (ArrayIndexOutOfBoundsException ex) {
                   System.out.println("Caught the ArrayIndexOutOfBoundsException :" + ex.getMessage());
                   ex.printStackTrace();
             } catch (Exception ex) {
                   System.out.println("Caught the Exception :" + ex.getMessage());
                   ex.printStackTrace();
             } finally {
                   System.out.println("I am finally here");
             }
       }
}

Output:

When you compile and run the program, you may notice any of the following outputs. I have shown all possible outputs in different runs. You may get a different order because the value of b is generated at random.

Case 1:
***Demonstration-3.Handling multiple Exceptions***
Current value of b is : 1
c=5
Caught the Exception :null
I am finally here
java.lang.NullPointerException       at java2e.chapter10.Demonstration3.main(Demonstration3.java:27)
Case 2:
***Demonstration-3.Handling multiple Exceptions***
Current value of b is : 2
c=2
Caught the ArrayIndexOutOfBoundsException :2
I am finally herejava.lang.ArrayIndexOutOfBoundsException: 2
at java2e.chapter10.Demonstration3.main(Demonstration3.java:23)
Case 3:
***Demonstration-3.Handling multiple Exceptions***
Current value of b is : 0
Caught the ArithmeticException :/ by zero
I am finally here
java.lang.ArithmeticException: / by zero at java2e.chapter10.Demonstration3.main(Demonstration3.java:16)
You can observe the following points from the output of the program:
  • When an exception is raised, only one catch block is executed. For example, if the block catch (ArithmeticException ex){..} can handle the exception, the block catch (Exception ex){..} does not need to handle the exception again.

  • In the preceding program, all types of exceptions can be caught inside the catch (Exception ex) block, and so this block must be placed as the last catch block. For example, in this case:
    • The ArithmeticException class derives from the RuntimeException class, which in turn derives from the Exception class.

    • The ArrayIndexOutOfBoundsException class derives from the IndexOutOfBoundsException class, which in turn derives from the RuntimeException class, which in turn derives from the Exception class.

Note

In Eclipse, you can hover your mouse pointer on the exception name and then choose the option “Open Declaration” to reveal the inheritance hierarchy.

Multiple catch Clauses

From Java 7 onward, you can use a different variation of the catch clause. Now a single catch block can be used to catch multiple exception types. The following code snippet shows how to use multiple catch clauses in a common block:
//Java 7 onward, you can write multiple catch clauses like the following
catch (ArithmeticException | ArrayIndexOutOfBoundsException ex){  System.out.println("Caught either ArithmeticException or ArrayIndexOutOfBoundsException :" + ex.getMessage());
                   ex.printStackTrace();
             }
10.2 Can you predict the output of the following?
package java2e.chapter10;
import java.util.Random;
public class Quiz1 {
       public static void main(String[] args) {
             System.out.println("***Quiz1.It is about how to place the catch clauses in the program.***");
             int a = 5;
             Random randomGenerator = new Random();
             // Will generate 0 to 2.
             int b = randomGenerator.nextInt(3);
             System.out.println("Current value of b is : " + b);
             int c = 0;
             try {
                   //Here b=0,it will raise an ArithmeticException
                   c = a / b;
                   System.out.println("c=" + c);
             }
             //Incorrect placement of following catch clause
             catch (Exception ex) {
                   System.out.println("Caught the Exception :" + ex.getMessage());
                   ex.printStackTrace();
             }
       //Java 7 onward, you can write multiple catch clauses like the //following
             catch (ArithmeticException | ArrayIndexOutOfBoundsException  ex) {
                   System.out.println("Caught either ArithmeticException or ArrayIndexOutOfBoundsException  :" + ex.getMessage());
                   ex.printStackTrace();
             }
       //Correct placement of the catch clause
             /*catch (Exception ex) {
                   System.out.println("Caught the Exception :" + ex.getMessage());
                   ex.printStackTrace();
             }*/finally {
                   System.out.println("I am finally here");
             }
       }
}
You’ll encounter a compile-time error: Unreachable catch block for ArithmeticException. It is already handled in the catch block for Exception, as shown in Figure 10-4.
../images/433474_2_En_10_Chapter/433474_2_En_10_Fig4_HTML.jpg
Figure 10-4

Unreachable catch block

Exceptions follow the inheritance hierarchy. Therefore, you need to place catch blocks properly. It has already been mentioned that the ArithmeticException class derives from the RuntimeException class, which in turn derives from the Exception class. For your easy understanding, follow the associated comments in the program.

Point To Remember

When you deal with multiple catch blocks, you need to place the more-specific exception clause first. In other words, you should place the catch blocks from most specific to most general.

It will be good to note that if you are familiar with C#, you may notice that it supports some additional variations of catch clauses. For example, in C#, you may notice the following variations of catch blocks:
catch ()
  {
      Console.WriteLine("Encountered an Exception");
  }
Or:
catch (WebException ex) when (ex.Status == WebExceptionStatus.Timeout)
 {
 //some code
 }

Q&A Session

10.3 Can I use try and finally only as follows?
try{
   //Some code
 }
finally{
   //Some code
}

Yes.

10.4 Then why do you need a catch block at all?

The catch block is used to handle the exception in some specified manner. At the same time, you must note that the actual use of finally is different. It has been previously mentioned that inside finally you should do housekeeping so that the application can be gracefully closed. For example, if a file is already opened, you should close it, or, if you already allocated some resources, those should be released inside this block (to prevent memory leaks). In Chapter 14, you will learn to use a try-with-resource statement, where by using the term “resource” we mean an object that must be closed after the program finishes execution.

10.5 What will happen if I encounter an exception inside a finally block?

You should not forget the purpose of finally, which basically is to close files, release occupied resources, etc. But if you put your erroneous logic in the finally block, you may encounter an exception again. (The solution is the same—you can guard against a probable exception in the finally block with a try-catch, try-finally, or try-catch-finally block. In fact, prior to Java 6, you may notice such usage to close a resource.)

Demonstration 4

Here is such an example for your ready reference. You can see that once you receive the exception (when b becomes 0 in finally block), as usual, the subsequent lines inside the finally block will not execute. For example, in the following case the line “I am at the end of finally block” is not appearing in the output.
package java2e.chapter10;
import java.util.Random;
public class Demonstration4 {
       public static void main(String[] args) {
             System.out.println("***Demonstration-4.Incorrect way of writing code inside the finally block***");
             try {
              System.out.println("I am inside the try block.");
             }
             finally {
                   System.out.println("I am at the beginning of finally block.");
                   int a = 5;
                   Random randomGenerator = new Random();
                   // Will generate 0 to 2.
                   int b = randomGenerator.nextInt(3);//Can produce 0
                   System.out.println("Current value of b is : " + b);
                   int c = a / b;
                   System.out.println("c=" + c);
                   System.out.println("I am at the end of finally block.");
             }
       }
}
Here is the output when the value of b is 0:
***Demonstration-4.Incorrect way of writing code inside the finally block***
I am inside the try block.
I am at the beginning of finally block.
Current value of b is : 0
Exception in thread "main" java.lang.ArithmeticException: / by zero
at java2e.chapter10.Demonstration4.main(Demonstration4.java:19)

It is also useful to note that if you kill or interrupt a thread, the finally block may not execute, even though other threads can run and make the application as a whole alive. You will learn about threads in Chapter 11.

Q&A Session

10.6 Up until now, you have given examples like ArrayIndexOutOfBoundsException, ArithmeticException, etc. How can I remember these names?

These are built-in exceptions in Java. All of these are already defined in the java.lang package. Since this package is the default package, you’ll get all these exceptions imported by default. Upon practice, you can remember their names. I personally take help from Eclipse. Similar IDEs can help you with this as well.

In this context, you can notice what the exception is that your default handler is throwing. From that report, you can get the name of the exception. For example, notice the output of our Demonstration 1, which is as follows:
***Demonstration-1.Exploring Exceptions.***
Exception in thread "main" java.lang.ArithmeticException: / by zero
at java2e.chapter10.Demonstration1.main(Demonstration1.java:9)

From this output, you know that an ArithmeticException is raised.

Throwing an Exception

Up until now, you have seen examples of handling exceptions thrown by the Java runtime system. When you process Java statements, you may encounter such exceptions due to the wrong logic, loopholes, and so forth. But there is an alternative way to raise an exception—you also have the freedom to throw an exception explicitly by using the throw keyword. This method is useful when you make your own application and want to control the exceptional situation.

When you use the throw keyword, you need to follow the basic format, which is as follows:
throw anObjectOfThrowable;

where anObjectOfThrowable must be an instance of the Throwable class or its subclass.

Demonstration 5

Consider the following program and corresponding output:
package java2e.chapter10;
class DemoClass {
       void thowingException() {
             System.out.println("I always throw a NullPointerException");
             throw new NullPointerException("Forcefully throwing a NullPointerException");
             // System.out.println("I'll never print this line");
       }
}
class Demonstration5 {
       public static void main(String[] args) {
             System.out.println("***Demonstration5.The use of 'throw' keyword*** ");
             DemoClass demo = new DemoClass();
             try {
                   demo.thowingException();
             } catch (Exception e) {
                    System.out.println(e.getMessage());
                   e.printStackTrace();
             }
       }
}
Output:
***Demonstration5.The use of 'throw' keyword***
I always throw a NullPointerException
Forcefully throwing a NullPointerException
java.lang.NullPointerException: Forcefully throwing a NullPointerException       at java2e.chapter10.DemoClass.thowingException(Demonstration5.java:6)
at java2e.chapter10.Demonstration5.main(Demonstration5.java:17)

Rethrowing an Exception

Sometimes you need to rethrow an exception; for example, when you want to write a log entry or when you want to send a new higher-level exception. When you rethrow an exception from a catch block, it is rethrown to the next enclosing try block. Demonstration 6 shows such an example.

Here is a sample format that you can use to rethrow an exception:
try{
  //some code
 }
catch(Exception ex){
 //some code, for example, you log the exception
 //Now rethrow it
 throw ex;
}

Demonstration 6

Consider the following:
package java2e.chapter10;
class Demonstration6 {
       static int c;
       static void divide(int a, int b) {
             try {
                   b--;
                   c = a / b;
                   // some code
             } catch (ArithmeticException ex) {
                   //Log it now
                   System.out.println("a= " + a + " b= " + b);//a=100,b=0
                   System.out.println("Caught an exception: " + ex.getMessage());
                   // Now rethrow it
                   throw ex;// rethrowing the exception
             }
       }
       public static void main(String[] args) {
             System.out.println("***Demonstration-6.Rethrowing an exception.***");
             int a = 100, b = 1;
             try {
             divide(a,b);
             }
             catch (Exception ex) {
                   System.out.println("Recaught the exception inside main() method.");
                   System.out.println("a= " + a + " b= " + b);//a=100,b=1
                   System.out.println("Here is the stackTrace :");
                   ex.printStackTrace();
             }
       }
}
Output:
***Demonstration-6.Rethrowing an exception.***
a= 100 b= 0
Message: / by zero
Recaught the exception inside main() method.
a= 100 b= 1
Here is the stackTrace :
java.lang.ArithmeticException: / by zero
       at java2e.chapter10.Demonstration6.divide(Demonstration6.java:10)
       at java2e.chapter10.Demonstration6.main(Demonstration6.java:25)

You can see why logging some additional details before you rethrow an exception is important. As soon as you encountered the exception, you logged it, and from that log you discovered that the divisor (b) became 0 in the divide() method. If you do not use the try-catch block inside the divide() method and you do not log the values of a and b immediately, then you are dependent on the catch block inside the main() method only. In a case like this, when you see the final log statements, you may wonder why you see this exception even if the value of b is 1.

Note

You’ll learn to create and use your own exception shortly, and you may combine the original exception with your custom exception message and then rethrow it for better readability.

10.7 Can you compile the following code fragment?
package java2e.chapter10;
class TestClass {
       // some code
}
class Quiz2 {
       void raiseException() {
             System.out.println("I love to throw an exception");
             try {
                   throw new TestClass();
             } catch (TestClass e) {
                   //some code
             }
       }
}
You will get a compiler error—No exception of type TestClass can be thrown; an exception type must be a subclass of Throwable—as shown in Figure 10-5.
../images/433474_2_En_10_Chapter/433474_2_En_10_Fig5_HTML.jpg
Figure 10-5

An exception type must be a subclass of Throwable

To remove the errors in the previous program, you can follow the compiler’s suggestion. For example, in this case, if you just make the following change in the previous code, the compiler will not complain:
class TestClass extends Throwable{
//remaining code as it is

Use of throws Keyword

Java supports both the keywords throw and throws. You have already seen the use of the throw keyword in Demonstrations 5 and 6. Before you use the throws keyword, you need to remember the following points:
  • A throws keyword will be needed to indicate all the exceptions that a method can throw. Otherwise, you’ll encounter compile-time errors (except for the next point).

  • The previous rule is not applicable for Error or RuntimeException or any of their subclasses.

  • You must remember that checked exceptions must be included in a method’s throws list.

The upcoming demonstrations will illustrate these points in detail.

Note

Q&A 10.8 can help you to differentiate the checked exceptions from the unchecked exceptions. I suggest you go through Demonstration 7 and Demonstration 8 before you enter into that discussion.

Demonstration 7

Consider the following:
package java2e.chapter10;
class TestClassException extends Throwable {
       String str;
       TestClassException(String str) {
             this.str = str;
       }
       public String getMessage() {
             return str;
       }
}
class DemoClass7 {
       void raiseException() throws TestClassException {
             throw new TestClassException("A TestClassException is raised");
       }
}
class Demonstration7 {
       public static void main(String[] args) {
             System.out.println("***Demonstration-7.The use of throws keyword*** ");
             DemoClass7 demo = new DemoClass7();
             try {
                   demo.raiseException();
             } catch (TestClassException e) {
                   System.out.println(e.getMessage());// A TestClassException is //raised
                   System.out.println("Here is the stacktrace:");
                   e.printStackTrace();
             }
       }
}
Output:
***Demonstration-7.The use of throws keyword***
A TestClassException is raised
Here is the stacktrace:
java2e.chapter10.TestClassException: A TestClassException is raised at java2e.chapter10.DemoClass7.raiseException(Demonstration7.java:17)at java2e.chapter10.Demonstration7.main(Demonstration7.java:27)
Now, go through the following points:
  • You can see that inside DemoClass7, the method raiseException() is throwing an exception, but I have not used a try-catch block around this code. Instead, I have added throws statements after the method names, as follows:

void raiseException() throws TestClassException {
  • It is used to confirm that this method has the capability of throwing the exception of type TestClassException. The class has a constructor to accept a String message, so you can provide a meaningful message when you intend to throw such exceptions.

  • If you ignore the throws clause and just use the following code in Demonstration 7:

void raiseException(){
your program will not compile. It will raise the errors shown in Figure 10-6.
../images/433474_2_En_10_Chapter/433474_2_En_10_Fig6_HTML.jpg
Figure 10-6

Compile-time error because TestClassException is not included in raiseException()’s throws list

You may have to do the same for the main() method if you do not surround the line of code demo.raiseException(); with a try-catch block. So, in that case, your main() method also could be like the following:
public static void main(String[] args) throws TestClassException {
//rest of the code

Demonstration 8

Now, consider the following demonstration. In this program, notice that TestClass8Exception derives from RuntimeException, and in this case I have not included the custom exception in the raiseException() method’s throws list. But the program still compiles.
package java2e.chapter10;
//The class derives from RuntimeException
class TestClass8Exception extends RuntimeException {
       String str;
       TestClass8Exception(String str) {
             this.str = str;
       }
       public String getMessage() {
             return str;
       }
}
class DemoClass8 {
       // This time it will NOT raise compilation error
       void raiseException() {
       throw new TestClass8Exception("A TestClass8Exception is raised");
       }
}
class Demonstration8 {
       public static void main(String[] args) {
             System.out.println("***Demonstration-8.The use of  an unchecked exception*** ");
             DemoClass8 demo = new DemoClass8();
             try {
                   demo.raiseException();
             } catch (TestClass8Exception e) {
                   System.out.println(e.getMessage());// A TestClassException is raised
                   System.out.println("Here is the stacktrace:");
                   e.printStackTrace();
             }
       }
}
Output:
***Demonstration-8.The use of  an unchecked exception***
A TestClass8Exception is raised
Here is the stacktrace:
java2e.chapter10.TestClass8Exception: A TestClass8Exception is raised at java2e.chapter10.DemoClass8.raiseException(Demonstration8.java:19)at java2e.chapter10.Demonstration8.main(Demonstration8.java:28)

You can see that TestClass8Exception is derived from RuntimeException. So, it is not a checked exception. This is why the compiler will not raise any errors when you do not include this exception in the method’s throws list.

Checked Versus Unchecked Exceptions

Now you understand the difference between checked and unchecked exceptions. I have already mentioned that there are some kinds of exceptions where either a throws clause needs to list all of the exceptions that a method can throw or you need to handle the scenario with a try-catch block. Otherwise, you’ll encounter compile-time errors. This is why these are called checked exceptions or compile-time exceptions. The remaining exceptions are termed unchecked exceptions.

Note

As I mentioned before, to understand the difference between checked exceptions and unchecked exceptions, you may need to visit Demonstration 7 and Demonstration 8 again.

The following list includes some of the checked exceptions:
  • ClassNotFoundException

  • NoSuchMethodException

  • NoSuchFieldException

  • InstantiationException

  • CloneNotSupportedException

  • IllegalAccessException

  • InterruptedException

Here are some unchecked exceptions:
  • ArithmeticException

  • ArrayIndexOutOfBoundsException

  • IndexOutOfBoundsException

  • SecurityException

  • NullPointerException

Point To Remember

If a method can throw a checked exception, then either the method should specify the exception using the throws keyword or it needs to handle the exception itself using try-catch block. Otherwise, you’ll encounter compile-time errors.

Q&A Session

10.8 I understand that checked exceptions are subclasses of Exception. You are also saying that unchecked exceptions are subclasses of RuntimeException. But from the hierarchy, I am seeing that RuntimeException is also a direct subclass of Exception. Then how they become unchecked exceptions?

Let’s see what JLS11 says about this. Under section 11.1.1, it says the following:

“The checked exception classes are all exception classes other than the unchecked exception classes. That is, the checked exception classes are Throwable and all its subclasses other than RuntimeException and its subclasses and Error and its subclasses.”

At the same time, it also says that “the unchecked exception classes are the run-time exception classes and the error classes.”

Following these rules, the Java compiler can detect the RuntimeException clearly. So, following the language specification, you can safely say that any exception that is a subclass of the RuntimeException or Error class is not a checked exception.

Discussion on Chained Exceptions

Sometimes you can receive an exception that may be caused by some other exception. So, you may be interested to know the original cause. The concept of chained exceptions comes into the picture in such a scenario.

Consider a very simple scenario of ArithmeticException, which you may receive when you divide an integer by 0. Sometimes your application can compute or update the divisor using various logic. So, when you receive this exception, the original cause may be the result of an I/O that can ultimately make the divisor zero.

Chained exceptions can help us to know about such exceptional scenarios, and at the same time they can point to the layer in which the actual error exists.

To allow chained exceptions, you have the following methods:
Throwable getCause() and
Throwable initCause(Throwable cause)
And the following constructors:
Throwable(Throwable cause)
Throwable(String msg, Throwable cause)

Demonstration 9

Consider the following demonstration and output. Do not worry about some commented portions. To make the program short and simple, these portions are ignored. You’ll learn shortly that you can extend the chain once you uncomment these portions of code.
package java2e.chapter10;
class OuterException extends RuntimeException {
       String str = null;
       OuterException(String str) {
             this.str = str;
       }
       public String toString() {
             return str;
       }
}
class InnerException extends RuntimeException {
       String str = null;
       InnerException(String str) {
             this.str = str;
       }
       public String toString() {
             return str;
       }
}
//Indtroducing this class to increase the depth
/*
 class SubInnerException extends RuntimeException {
   String str = null; *
 SubInnerException(String str) {
this.str = str;
 }
  public String toString() {
return str;
}
}
 */
class Demo9Class {
       void raiseException() // throws clause not necessary now
       {
             OuterException outer = new OuterException("An OuterException is raised.");
             InnerException inner = new InnerException("It is caused by an InnerException.");
             /*SubInnerException subInner = new SubInnerException("It is again caused by an SubInnerException.");*/
             outer.initCause(inner);
             // inner.initCause(subInner);
             throw outer;
       }
}
class Demonstration9 {
       public static void main(String args[]) {
             System.out.println("***Demonstration-9.A chained exception demo*** ");
             Demo9Class demo = new Demo9Class();
             try {
                   demo.raiseException();
             } catch (OuterException e) {
                   System.out.println(e);
                   System.out.println("Here is the details:" + e.getCause());
                   System.out.println("Here is the stack trace :");
                   e.printStackTrace();
             }
       }
}
Output:
An OuterException is raised.
Here is the details:It is caused by an InnerException.
Here is the stack trace :
An OuterException is raised.at java2e.chapter10.Demo9Class.raiseException(Demonstration9.java:41)at java2e.chapter10.Demonstration9.main(Demonstration9.java:55)
Caused by: It is caused by an InnerException.       at java2e.chapter10.Demo9Class.raiseException(Demonstration9.java:42)
       ... 1 more
You can carry on to the depth you want. It is recommended that you do not make a very long chain, because that can lead to a poor design. For simple demonstration purposes, as said before, if you uncomment the commented portions of the code, you will notice the following output:
***Demonstration-9.A chained exception demo***
An OuterException is raised.
Here is the details:It is caused by an InnerException.
Here is the stack trace :
An OuterException is raised.
       at java2e.chapter10.Demo9Class.raiseException(Demonstration9.java:46)
       at java2e.chapter10.Demonstration9.main(Demonstration9.java:60)
Caused by: It is caused by an InnerException.
       at java2e.chapter10.Demo9Class.raiseException(Demonstration9.java:47)
       ... 1 more
Caused by: It is again caused by an       SubInnerException.
       at java2e.chapter10.Demo9Class.raiseException(Demonstration9.java:48)
       ... 1 more
10.9 Can you compile the following program?
package java2e.chapter10;
import java.util.Random;
class OuterQuiz3Exception extends Exception {
       String str = null;
       OuterQuiz3Exception(String str) {
             this.str = str;
       }
       public String toString() {
             return str;
       }
}
class InnerQuiz3Exception extends OuterQuiz3Exception {
       InnerQuiz3Exception(String str) {
             super(str);
       }
       public String toString() {
             return str;
       }
}
class Quiz3Class {
       // InnerQuiz3Exception is not needed to include in the throws list //because it is a subclass of OuterQuiz3Exception
       void raiseException() throws OuterQuiz3Exception {  //throws clause is necessary now
             OuterQuiz3Exception outer = new OuterQuiz3Exception("An OuterQuiz3Exception is raised.");
             InnerQuiz3Exception inner = new InnerQuiz3Exception("An InnerQuiz3Exception is raised.");
             Random randomGenerator = new Random();
             // Will generate 0 to 1.
             int b = randomGenerator.nextInt(2);
             System.out.println("In this case, b="+ b);
             if (b == 0) {
                   throw outer;
             } else
                   throw inner;
       }
}
class Quiz3 {
       public static void main(String[] args) throws OuterQuiz3Exception {
             System.out.println("***Quiz3*** ");
             Quiz3Class demo = new Quiz3Class();
             try {
                   demo.raiseException();
             } catch (OuterQuiz3Exception e) {
                   System.out.println(e);
                   System.out.println("Here is the stack trace :");
                   e.printStackTrace();
             }
       }
}
Yes, the program will compile, and here is one possible output. (When b = 0).
***Quiz3***
In this case, b=0
An OuterQuiz3Exception is raised.
Here is the stack trace :
An OuterQuiz3Exception is raised.
at java2e.chapter10.Quiz3Class.raiseException(Quiz3.java:33)
at java2e.chapter10.Quiz3.main(Quiz3.java:52)
Here are some important points to note:
  • The OuterQuiz3Exception class inherits from the Exception class. So, it is a checked exception. Since you have not used a try-catch block in the raiseException() method, the throws clause is necessary for the raiseException() method now.

  • You needed to list all the exceptions in the throws list that the method can throw. But InnerQuiz3Exception is a subclass of OuterQuiz3Exception. So, only the inclusion of OuterQuiz3Exception in the throws list of the raiseException() method was sufficient for you. But if you include InnerQuiz3Exception also, there will be no compiler issue.

  • I am generating a random number between 0 (inclusive) and 2 (exclusive), so output may vary when an InnerQuiz3Exception is thrown (i.e., when b = 1).

Creating a Custom Exception

You have already seen some common uses of Java’s built-in exceptions. These are very handy, and, in most cases, they can serve your needs. But sometimes you may want to define your own exception class to get messages that are more meaningful to you. So, you may want to create your own exceptions to handle some specific situations in your application.

Creating a custom exception is easy. But before you proceed further, you should remember the following points:
  • A common practice to create a user-defined exception class is to extend from the Exception class. You have learned that Exception is a subclass of the Throwable class. So, you can override or use the methods defined in the Throwable classes.

  • The Exception class does not have any method specific to it. But here you’ll see different overloaded version of constructors; for example, you will notice the presence of the following constructors. In the discussion of chained exceptions, you got to know about two of them. In the upcoming demonstration, you will see the use of another two:

public Exception() {}
public Exception(String message) {}
public Exception(String message, Throwable cause) {}
public Exception(Throwable cause) {}
protected Exception(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {}
  • Following convention, it is suggested that when you create your own exception, the class name should end with the word Exception.

Demonstration 10

Let’s start. For simplicity, assume that you need to consider two integer inputs only. You will display the sum of the integers if and only if the aggregate is less than or equal to 100. If it is not less than 100, you’ll throw your custom exception.
package java2e.chapter10;
class SumGreaterThan100Exception extends Exception {
       SumGreaterThan100Exception() {
             System.out.println("Greater than 100.");
       }
       SumGreaterThan100Exception(String msg) {
             super(msg);
       }
}
interface DemoInterface {
       int sum(int x, int y) throws SumGreaterThan100Exception;
}
class Demo10Class implements DemoInterface {
       public int sum(int x, int y) throws SumGreaterThan100Exception {
             int sumofIntegers = x + y;
             if (sumofIntegers <= 100) {
                   System.out.println(" Here first number="+ x + " and second Number="+y);
                   return sumofIntegers;
             } else {
                   System.out.println(" Now first number="+ x + " and second Number="+y);
                   throw new SumGreaterThan100Exception("Sum is greater than 100.");
                   //throw new SumGreaterThan100Exception();
             }
       }
}
class Demonstration10 {
       public static void main(String args[]) {
             System.out.println("***Demonstration-10.Creating a custom exception*** ");
             Demo10Class demo = new Demo10Class();
             try {
                   int result = demo.sum(10, 50);// ok
                   System.out.println("Sum of 10 and 50 is : " + result);
                   // Now the sum is greater than 100, so, it will raise //the  custom exception.
                   result = demo.sum(50, 70);
                   System.out.println("Sum of 50 and 70 is :  " + result);
             } catch (SumGreaterThan100Exception e) {
                   System.out.println("Caught the custom exception : " + e);
                   e.printStackTrace();
             }
       }
}
Output:
***Demonstration-10.Creating a custom exception***
 Here first number=10 and second Number=50
Sum of 10 and 50 is : 60
 Now first number=50 and second Number=70
Caught the custom exception : java2e.chapter10.SumGreaterThan100Exception: Sum is greater than 100.
java2e.chapter10.SumGreaterThan100Exception: Sum is greater than 100.
       at java2e.chapter10.Demo10Class.sum(Demonstration10.java:25)
       at java2e.chapter10.Demonstration10.main(Demonstration10.java:39)
You have used the parameterized constructor. If you want to use the default constructor (which is commented here), there is a slightly different message.
***Demonstration-10.Creating a custom exception***
 Here first number=10 and second Number=50
Sum of 10 and 50 is : 60
 Now first number=50 and second Number=70
Greater than 100.
Caught the custom exception : java2e.chapter10.SumGreaterThan100Exception
java2e.chapter10.SumGreaterThan100Exception
       at java2e.chapter10.Demo10Class.sum(Demonstration10.java:26)
       at java2e.chapter10.Demonstration10.main(Demonstration10.java:39)

Q&A Session

10.10 I understand that Java does not support pointers. But I am seeing that it supports NullPointerException. I am confused.

You need to understand the scenario. When you perform some illegal operations (for example, when you invoke a method incorrectly or try to access some fields incorrectly) through a null object, you encounter this exception. This exception generally indicates that you are treating a null object as an actual object, so your intended operation is illegal. Yes, some developers believe that something like NullReferenceException could be a better name for this type of exception.

At the same time, you also remember that Java designers believe that the use of pointers is one of the primary sources of injecting bugs into the application. So, they do not support any pointer datatypes.

10.11 It appears to me that I can suppress errors with exceptions. Am I right?

Yes. But it is never intended. Consider the following demonstration.

Demonstration 11

In this example, when you get the value of b as 0 (which is randomly generated), instead of reporting the true issue, you suppress the error by printing c=7, which is a total misuse of this feature.
package java2e.chapter10;
import java.util.Random;
public class Demonstration11 {
       public static void main(String[] args) {
             System.out.println("***Demonstration-11.Incorrect use of try-catch block*** ");
             int a = 10;
             Random randomGenerator = new Random();
             // Will generate 0 to 2.
             int b = randomGenerator.nextInt(3);
             System.out.println("b=" + b);
             int c = 0;
             try {
                   c = a / b;
                   System.out.println("c=" + c);
             } catch (ArithmeticException ex) {
                   // printing c=7, after catching the exception
                   System.out.println("c=" + 7);
             }
       }
}
Here is one possible output. You will get this output when you encounter an ArithmeticException and are using a try-catch block to suppress the true message, which is incorrect.
***Demonstration-11.Incorrect use of try-catch block***
b=0
c=7

Q&A Session

10.12 Should I make my custom exceptions checked (or unchecked)?

If you can do something to recover from an exception, make it a checked exception; otherwise, make it unchecked. Based on your requirements, you can follow the approach of Demonstration 7 or Demonstration 8 to make your custom exception checked or unchecked.

10.13 You said earlier, “You may combine the original exception with your custom exception message and then rethrow it for better readability.” Can you show me a demonstration?

In the Java programming world, it’s very common to catch a built-in exception and rethrow the same exception via a custom exception for better readability and understanding. I discussed this regarding Demonstration 6.

Demonstration 12

Let’s modify Demonstration 6 so that you can experience the same:
package java2e.chapter10;
//A custom exception
class InvalidIntegerInputException extends Exception {
        InvalidIntegerInputException(String msg,Throwable causeEx) {
             super(msg, causeEx);
       }
}
class Demonstration12 {
       static int c;
       static void divide(int a, int b) throws  InvalidIntegerInputException{
             try {
                   b--;
                   c = a / b;
                   // some code
             } catch (ArithmeticException ex) {
                   //Log it now
                   System.out.println("a= " + a + " b= " + b);//a=100,b=0
                   System.out.println("Message: " + ex.getMessage());
                   // Now rethrow it
                   throw new InvalidIntegerInputException(" The divisor becomes zero", ex);
             }
       }
       public static void main(String[] args) {
             System.out.println("***Demonstration-12.Rethrowing an exception which is wrapped in a custom exception.***");
             System.out.println("Actually, we are modifying the demonstration-6.");
             int a = 100, b = 1;
             try {
             divide(a,b);
             }
             catch (Exception ex) {
                   System.out.println("Recaught the exception inside main() method.");
                   System.out.println("a= " + a + " b= " + b);//a=100,b=1
                   System.out.println("Here is the stackTrace :");
                   ex.printStackTrace();
             }
       }
}
Output:
***Demonstration-12.Rethrowing an exception which is wrapped in a custom exception.***
Actually, we are modifying the demonstration-6.
a= 100 b= 0
Message: / by zero
Recaught the exception inside main() method.
a= 100 b= 1
Here is the stackTrace :
java2e.chapter10.InvalidIntegerInputException:  The divisor becomes zero
       at java2e.chapter10.Demonstration12.divide(Demonstration12.java:23)
       at java2e.chapter10.Demonstration12.main(Demonstration12.java:32)
Caused by: java.lang.ArithmeticException: / by zero
       at java2e.chapter10.Demonstration12.divide(Demonstration12.java:16)
       ... 1 more

Notice the message “The divisor becomes zero” appeared in your output, which is the actual cause of the ArithmeticException in this program.

Summary

This chapter answered the following questions:
  • What is an exception?

  • How can you handle errors in our program?

  • What are the common keywords used when we deal with exceptions in Java?

  • How should you place try, catch, and finally blocks in your program, and what is their purpose?

  • What are the different variations of the catch clause?

  • How can you throw an exception?

  • How can you rethrow an exception?

  • How is throw different from throws?

  • How can you classify exceptions? How are checked exceptions different from unchecked exceptions?

  • How can you make chained exceptions?

  • How do you make a custom exception?

  • How can you catch a built-in exception and combine it with a custom exception?

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

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