More on Exception Handling
Tags: java, programming, learning, beginners
Published: 2024-06-14 09:36:31

A handler for an exception is found by propagating the exception backward through a chain of method calls, starting from the current method. Java’s exception-handling model is based on three operations: _declaring an exception_, _throwing an exception_, and _catching an exception_, as shown in figure below.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/b11f5apj5v4w9o6ek3my.png)

## Declaring Exceptions

In Java, the statement currently being executed belongs to a method. The Java interpreter invokes the **main** method to start executing a program. Every method must state the types of checked exceptions it might throw. This is known as _declaring exceptions_. Because system errors and runtime errors can happen to any code, Java does not require that you declare **Error** and **RuntimeException** (unchecked exceptions) explicitly in the method. However, all other exceptions thrown by the method must be explicitly declared in the method header so that the caller of the method is informed of the exception.

To declare an exception in a method, use the **throws** keyword in the method header, as in this example:

`public void myMethod() throws IOException`

The **throws** keyword indicates that **myMethod** might throw an **IOException**. If the method might throw multiple exceptions, add a list of the exceptions, separated by commas, after **throws**:

`public void myMethod()
 throws Exception1, Exception2, ..., ExceptionN`

If a method does not declare exceptions in the superclass, you cannot override it to declare exceptions in the subclass.

## Throwing Exceptions

A program that detects an error can create an instance of an appropriate exception type and throw it. This is known as _throwing an exception_. Here is an example: Suppose the program detects that an argument passed to the method violates the method contract (e.g., the argument must be nonnegative, but a negative argument is passed); the program can create an instance of **IllegalArgumentException** and throw it, as follows:

`IllegalArgumentException ex =
new IllegalArgumentException("Wrong Argument");
throw ex;`

Or, if you prefer, you can use the following:

`throw new IllegalArgumentException("Wrong Argument");`

**IllegalArgumentException** is an exception class in the Java API. In general, each exception class in the Java API has at least two constructors: a no-arg constructor, and a constructor with a **String** argument that describes the exception. This argument is called the exception message, which can be obtained using **getMessage()**. The keyword to declare an exception is **throws**, and the keyword to throw an exception is **throw**.

## Catching Exceptions

You now know how to declare an exception and how to throw an exception. When an exception is thrown, it can be caught and handled in a **try-catch** block, as follows:

`try {
 statements; // Statements that may throw exceptions
}
catch (Exception1 exVar1) {
 handler for exception1;
}
catch (Exception2 exVar2) {
 handler for exception2;
}
...
catch (ExceptionN exVarN) {
 handler for exceptionN;
}`

If no exceptions arise during the execution of the **try** block, the **catch** blocks are skipped.

If one of the statements inside the **try** block throws an exception, Java skips the remaining statements in the **try** block and starts the process of finding the code to handle the exception. The code that handles the exception is called the _exception handler_; it is found by _propagating the exception_ backward through a chain of method calls, starting from the current method. Each **catch** block is examined in turn, from first to last, to see whether the type of the exception object is an instance of the exception class in the **catch** block. If so, the exception object is assigned to the variable declared, and the code in the **catch** block is executed. If no handler is found, Java exits this method, passes the exception to the method that invoked the method, and continues the same process to find a handler. If no handler is found in the chain of methods being invoked, the program terminates and prints an error message on the console. The process of finding a handler is called _catching an exception_.

Suppose the **main** method invokes **method1**, **method1** invokes **method2**, **method2** invokes **method3**, and method3 throws an exception, as shown in Figure below. Consider the following scenario:

- If the exception type is **Exception3**, it is caught by the **catch** block for handling exception **ex3** in **method2**. **statement5** is skipped, and **statement6** is executed.
- If the exception type is **Exception2**,** method2** is aborted, the control is returned to **method1**, and the exception is caught by the **catch** block for handling exception **ex2** in **method1**. **statement3** is skipped, and **statement4** is executed.
- If the exception type is **Exception1**, **method1** is aborted, the control is returned to the **main** method, and the exception is caught by the **catch** block for handling exception **ex1** in the **main** method. **statement1** is skipped, and **statement2** is executed.
- If the exception type is not caught in **method2**, **method1**, or **main**, the program terminates, and **statement1** and **statement2** are not executed.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fhsw3bwbjyoj0t0q3nin.png)

Various exception classes can be derived from a common superclass. If a **catch** block catches exception objects of a superclass, it can catch all the exception objects of the subclasses of that superclass.

The order in which exceptions are specified in **catch** blocks is important. A compile error will result if a catch block for a superclass type appears before a catch block for a subclass type. For example, the ordering in (a) on the next page is erroneous, because **RuntimeException** is a subclass of **Exception**. The correct ordering should be as shown in (b).

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3gz2bb5wpbs6yqpkiw1u.png)

Java forces you to deal with checked exceptions. If a method declares a checked exception (i.e., an exception other than **Error** or **RuntimeException**), you must invoke it in a **try-catch** block or declare to throw the exception in the calling method. For example, suppose that method **p1** invokes method **p2**, and **p2** may throw a checked exception (e.g., **IOException**); you have to write the code as shown in (a) or (b) below.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/raqpbbiwo6px5z2pho1t.png)

You can use the multi-catch feature to simplify coding for the exceptions
with the same handling code. The syntax is:

`catch (Exception1 | Exception2 | ... | Exceptionk ex) {
// Same code for handling these exceptions
}`

Each exception type is separated from the next with a vertical bar (**|**). If one of the exceptions is caught, the handling code is executed.

## Getting Information from Exceptions

An exception object contains valuable information about the exception. You may use the following instance methods in the **java.lang.Throwable** class to get information regarding the exception, as shown in Figure below. The **printStackTrace()** method prints stack trace information on the console. The **getStackTrace()** method provides programmatic access to the stack trace information printed by **printStackTrace()**.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/66sl9rpzcqzsfvvcf4zz.png)

The program below gives an example that uses the methods in **Throwable** to display exception information. Line 7 invokes the **sum** method to return the sum of all the elements in the array. There is an error in line 26 that causes the **ArrayIndexOutOfBoundsException**, a subclass of **IndexOutOfBoundsException**. This exception is caught in the **try-catch** block. Lines 10, 11, and 12 display the stack trace, exception message, and exception object and message using the **printStackTrace()**, **getMessage()**, and **toString()** methods. Line 15 brings stack trace elements into an array. Each element represents a method call. You can obtain the method (line 17), class name (line 18), and exception line number (line 19) for each element.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/zxano2dkmwzyo3xbzofl.png)

`java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5
	at demo.TestException.sum(TestException.java:27)
	at demo.TestException.main(TestException.java:7)

Index 5 out of bounds for length 5

java.lang.ArrayIndexOutOfBoundsException: Index 5 out of bounds for length 5

Trace Info Obtained from getStackTrace
method sum(demo.TestException:27)
method main(demo.TestException:7)`

## Example: Declaring, Throwing, and Catching Exceptions

This example demonstrates declaring, throwing, and catching exceptions by modifying the **setRadius** method in the **Circle** class in, CircleWithPrivateDataFields.java ([here](https://dev.to/paulike/data-field-encapsulation-4i7b)). The new **setRadius** method throws an exception if the radius is negative.

The program below defines a new circle class named **CircleWithException**, which is the same as **CircleWithPrivateDataFields** except that the **setRadius(double newRadius)** method throws an **IllegalArgumentException** if the argument **newRadius** is negative.

```
package demo;

public class CircleWithException {
	/** The radius of the circle */
	private double radius;
	
	/** The number of the objects created */
	private static int numberOfObjects = 0;
	
	/** Construct a circle with radius 1 */
	public CircleWithException() {
		this(1.0);
	}
	
	/** Construct a circle with a specified radius */
	public CircleWithException(double newRadius) {
		setRadius(newRadius);
		numberOfObjects++;
	}
	
	/** Return radius */
	public double getRadius() {
		return radius;
	}
	
	/** Set a new radius */
	public void setRadius(double newRadius) throws IllegalArgumentException {
		if(newRadius > 0)
			radius = newRadius;
		else
			throw new IllegalArgumentException("Radius cannot be negative");
	}
	
	/** Return numberOfObjects */
	public static int getNumberOfObjects() {
		return numberOfObjects;
	}
	
	/** Return the area of this circle */
	public double findArea() {
		return radius * radius * 3.14159;
	}
}

```

A test program that uses the new **Circle** class is given in the program below.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/a4y0vcwrcoc287rl9vf1.png)

The original **Circle** class remains intact except that the class name is changed to **CircleWithException**, a new constructor **CircleWithException(newRadius)** is added, and the **setRadius** method now declares an exception and throws it if the radius is negative.

The **setRadius** method declares to throw **IllegalArgumentException** in the method header (lines 27–32 in CircleWithException.java). The **CircleWithException** class would still compile if the **throws IllegalArgumentException** clause (line 27) were removed from the method declaration, since it is a subclass of **RuntimeException** and every method can throw **RuntimeException** (an unchecked exception) regardless of whether it is declared in the method header.

The test program creates three **CircleWithException** objects—**c1**, **c2**, and **c3**—to test how to handle exceptions. Invoking **new CircleWithException(-5)** (line 8 in TestCircleWithException.java) causes the **setRadius** method to be invoked, which throws an **IllegalArgumentException**, because the radius is negative. In the **catch** block, the type of the object **ex** is **IllegalArgumentException**, which matches the exception object thrown by the **setRadius** method, so this exception is caught by the **catch** block.

The exception handler prints a short message, **ex.toString()** (line 12 in TestCircleWithException.java), about the exception, using **System.out.println(ex)**.

Note that the execution continues in the event of the exception. If the handlers had not caught the exception, the program would have abruptly terminated.

The test program would still compile if the **try** statement were not used, because the method throws an instance of **IllegalArgumentException**, a subclass of **RuntimeException** (an unchecked exception). If a method throws an exception other than **RuntimeException** or **Error**, the method must be invoked within a try-catch block.