Exception

The Java API has exception classes that are mapped for every exception that can occur while using the Java API. If an exception occurs during execution, the JVM creates an object from the corresponding exception class and throws it to the code that generated the exception. The JVM is responsible for throwing an exception object when an exception occurs. It is up to the programmer to handle how the exception object is handled. Programmers can control the generated exception object with appropriate use of the try, catch, and finally keywords. In the Java programming world, if an escalator suddenly stops in the middle, you can program to walk a human object in a catch block1.

What are errors in Java?
Exceptions are strictly errors, but Java distinguishes between exceptions and errors. An error in Java is "an error that can not be controlled programmatically". If an error occurs, the program is terminated. When an error occurs, there is little that the programmer can do.

Control Exceptions

Exception classes have a hierarchical structure. Exception classes are the top class of all exception classes. The following is an example of dealing with exceptions.

try {
   //Code that can cause exceptions A and B
   //A is hierarchically above B
} catch (B e) { //e is the reference to the B exception object
   //Code executed when the exception B occurs
} catch (A e) {
   //Code executed when the exception A occurs
} finally {
   //The code that must be executed, with or without an exception
}
try block
It is a block that encloses code that can cause exceptions. It can not be used alone, and it is used with a catch block or a finally block.
catch block
Just as when an error occurs, the program terminates unless an appropriate action is taken by the programmer when an exception occurs. To handle exceptions that occur in a try block, you must use the catch block appropriately within your code. To fine-tune the exception as in the example above, use multiple catch blocks.
finally block
Whether an exception occurs or not, it is a block that must be executed. You can not use finally blocks in multiple.

Exception mechanism

Assume that method1 calls method2 and method2 calls method3, as shown below.
Exception Mechanism
If there is an exception in method3 and there is a catch block that catches an exception in method3 body, the program continues without stopping. If there is no catch block that catches the exception that occurred in method3 body, the exception that occurred in method3 is passed to method2 that invoked method3. If method2 does not have a catch block catching exceptions passed in method3, this exception is passed to method1, which called method2. When this exception is passed to the JVM that called the main method, the program is terminated. Such termination is an abnormal termination.

Exception class hierarchy

Exception API

Common exceptions

Exception Example
ArithmeticException int a = 12/0;
NullPointerException Integer d = null;
int val = d.intValue();
NegativeArraySizeException int arr = new int[-1];
ArrayIndexOutOfBoundException int[] arr = new int[2];
arr[2] = 1;

throws SomethingException in the method declaration

You can attach an exception class after the throws keyword in the method declaration section. If the code that calls this method does not have a code snippet that properly handles exceptions defined after throws, a compilation error will occur.

Suppose you have the following method.

public void someMethod() throws SomeException {
	//..Omitted.
}

If you need to create a method with code that calls someMethod() on the method body, you have to choose one of the two below to avoid compilation errors.

1
public void myMethod() throws SomeException {
	someMethod();
	//..Omitted.
}
2
public void myMethod() {
	
	try {
		someMethod();
	} catch (SomeException e) {
		//..Omitted.
	}
	
}

1:
If SomeException is a RuntimeException or a subclass of RuntimeException2, You can omit throws SomeException from the myMethod method declaration section.

2:
If SomeException is a RuntimeException or a subclass of RuntimeException, Even if try ~ catch is omitted, there is no compilation error. However, you can not catch exceptions that occur.

Examples

The following is an example of an unchecked exception.

package net.java_school.exception;

public class Test {

	public static void method1() {
		method2();
	}
        
	public static void method2() {
		method3();
	}

	public static void method3() {
		int a = 12 / 0;
		System.out.println(a);
	}
        
	public static void main(String[] args) {
		method1();
		System.out.println("Normal Termination");
	}

}

When you run the example, the ArithmeticException exception object reaches the JVM. The JVM will sequentially print the stack on which the exception occurred, as shown below. The important thing is that it is not a normal Termination. For reference, the main method is implemented to print "Normal Termination" if executed to the last line.

Exception in thread "main" java.lang.ArithmeticException: / by zero
	at net.java_school.exception.Test.method3(Test.java:14)
	at net.java_school.exception.Test.method2(Test.java:10)
	at net.java_school.exception.Test.method1(Test.java:6)
	at net.java_school.exception.Test.main(Test.java:19)

Modify the method3() to handle exceptions using try ~ catch.

package net.java_school.exception;

public class Test {

	public static void method1() {
		method2();
	}

	public static void method2() {
		method3();
	}

	public static void method3() {
		try {
			int a = 12 / 0;
			System.out.println(a);
		} catch (ArithmeticException e) {
			System.out.println(e.getMessage());
		}
	}

	public static void main(String[] args) {
		method1();
		System.out.println("Normal Termination");	
	}

}

System.out.println(e.getMessage()); prints / by zero.

/ by zero
Normal Termination

Retrieve the previous code and modify the method2() to handle the exception using try ~ catch.

package net.java_school.exception;

public class Test {

	public static void method1() {
		method2();
	}
	
	public static void method2() {
		try {
			method3();
		} catch (ArithmeticException e) {
			System.out.println(e.getMessage());
		}
	}
	
	public static void method3() {
		int a = 12 / 0;
		System.out.println(a);
	}
	
	public static void main(String[] args) {
		method1();
		System.out.println("Normal Termination");
	}

}

The exception object from method3() is passed to method2(). You can think of similar to returning a return value.

/ by zero
Normal Termination

Revert the previous code and modify method1() to handle the exception.

package net.java_school.exception;

public class Test {

	public static void method1() {
		try {
			method2();
		} catch (ArithmeticException e) {
			System.out.println(e.getMessage());
		}
	}
	
	public static void method2() {
		method3();
	}
	
	public static void method3() {
		int a = 12 / 0;
		System.out.println(a);
	}
	
	public static void main(String[] args) {
		method1();
		System.out.println("Normal Termination");
	}

}

The output is the same, but the exception object is passed to method1() and properly handles exceptions in method1(), thus avoiding abnormal termination.

/ by zero
Normal Termination

Reverses the previous code and modifies the main method to handle the exception.

package net.java_school.exception;

public class Test {

	public static void method1() {
		method2();
	}
	
	public static void method2() {
		method3();
	}
	
	public static void method3() {
		int a = 12 / 0;
		System.out.println(a);
	}
	
	public static void main(String[] args) {
		try {
			method1();
		} catch (ArithmeticException e) {
			System.out.println(e.getMessage());
		}
	
		System.out.println("Normal Termination");
	}

}

The output is the same, but the exception object is passed to the main method. The main method handles the exception object properly, so it exits normally.

/ by zero
Normal Termination

The try block does not have to be used with a catch block. However, if there is no catch block, the exception object can not be caught. The following example removes the catch block. The try block can not be used alone, so I used a finally block instead.

package net.java_school.exception;

public class Test {

	public static void method1() {
		method2();
	}
	
	public static void method2() {
		method3();
	}
	
	public static void method3() {
		int a = 12 / 0;
		System.out.println(a);
	}
	
	public static void main(String[] args) {
		try {
			method1();
		} finally {
			System.out.println("Executing a finally block");
		}
		
		System.out.println("Normal Termination");
	}

}

Since there is no catch block, the exception object is passed to the JVM and the program terminates abnormally. We can see in the output that the finally block is executed and the exception is passed to the JVM.

Executing a finally block
Exception in thread "main" java.lang.ArithmeticException: / by zero
        at net.java_school.exception.Test.method3(Test.java:14)
        at net.java_school.exception.Test.method2(Test.java:10)
        at net.java_school.exception.Test.method1(Test.java:6)
        at net.java_school.exception.Test.main(Test.java:20)

Add a catch block to handle exceptions. Modify the catch block so that it catches any exception in the try block.

package net.java_school.exception;

public class Test {

	public static void method1() {
		method2();
	}
	
	public static void method2() {
		method3();
	}
	
	public static void method3() {
		int a = 12 / 0;
		System.out.println(a);
	}
	
	public static void main(String[] args) {
		try {
			method1();
		} catch (Exception e) {
			System.out.println(e.getMessage());
		} finally {
			System.out.println("Executing a finally block");
		}
		
		System.out.println("Normal Termination");
	}

}

Since an exception has occurred, the catch block is executed and the finally block is executed. Since the catch block has caught the exception object, the exception object is no longer passed anywhere.

/ by zero
Executing a finally block
Normal Termination

The catch block can be overwritten as if ~ else if ~ else if .... However, you should pay attention to the hierarchical relationship of the exception class when using it in this way. When an exception occurs in a try block, the catch block tries to catch the exception in order from top to bottom. If the preceding exception class is hierarchically above the exception class that follows, then there is no reason for the code to go down. So the following code causes a compile error.

package net.java_school.exception;

public class Test {

	public static void method1() {
		method2();
	}
	
	public static void method2() {
		method3();
	}
	
	public static void method3() {
		int a = 12 / 0;
		System.out.println(a);
	}
	
	public static void main(String[] args) {
		try {
			method1();
		} catch (Exception e) {
			System.out.println(e.getMessage());
		} catch (ArithmeticException e) { 
			System.out.println(e.getMessage());
		} finally {
			System.out.println("Executing a finally block");
		}
		
		System.out.println("Normal Termination");
	}

}

You can compile by changing the order of the catch block as shown below.

package net.java_school.exception;

public class Test {

	public static void method1() {
		method2();
	}
	
	public static void method2() {
		method3();
	}
	
	public static void method3() {
		int a = 12 / 0;
		System.out.println(a);
	}
	
	public static void main(String[] args) {
		try {
			method1();
		} catch (ArithmeticException e) {
			System.out.println(e.getMessage());
		} catch (Exception e) {
			System.out.println(e.getMessage());
		} finally {
			System.out.println("Executing a finally block");
		}
		
		System.out.println("Normal Termination");
	}

}

When you run the program, a catch (ArithmeticException e) {..} block is executed and e.getMessage() of this block is printed. The finally block is then executed and the last line of the main is executed.

/ by zero
Executing a finally block
Normal Termination

Modify the same example to the checked exception example. You will get a compile error in the highlighted section. The compilation error message is Unhandled exception type ClassNotFoundException.

package net.java_school.exception;

public class Test {

	public static void method1() {
		method2();
	}
	
	public static void method2() {
		method3();
	}
	
	public static void method3() {
		Class.forName("java.lang.Boolean");
	}
	
	public static void main(String[] args) {
		method1();
		System.out.println("Normal Termination");
	}

}

Class.forName ("string"); is not a new Java grammar. There is a java.lang.Class in the Java API, and this class has a forName() static method. The forName() method takes a string as an argument, which is the full name of the Java class (FQCN). The forName() method causes the class loader to load the class corresponding to the argument. If the class loader does not find the class corresponding to the argument, the JVM creates a ClassNotFoundException exception object and throws it to the Class.forName ("string") line. In the example, the class corresponding to "java.lang.Boolean" passed as argument to forName() belongs to the Java API, so the class loader looks for this class without the cp or classpath option. The purpose of this class in this example is not important. The important thing is the following. There is a throws ClassNotFoundException in the class declaration of the forName() method. Because ClassNotFoundException is a checked exception that does not inherit RuntimeException, the method that calls forName() must implement code that handles this exception. Let's modify the code using the code assist feature in Eclipse. If you put your cursor in Class.forName(), Eclipse will give you a solution. If you click on the second method, the code changes as below and the compile error disappears.

package net.java_school.exception;

public class Test {

	public static void method1() {
		method2();
	}
	
	public static void method2() {
		method3();
	}
	
	public static void method3() {
		try {
			Class.forName("java.lang.Boolean");
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		method1();
		System.out.println("Normal Termination");
	}

}

Execution normally ends without any exception. This is because a class called java.lang.Boolean exists in the Java API.

Normal Termination

Reverses the previous code and handles the exception in the first way Eclipse suggests. Then a compilation error occurs on method3(); line in method2().

package net.java_school.exception;

public class Test {

	public static void method1() {
		method2();
	}
	
	public static void method2() {
		method3();
	}
	
	public static void method3() throws ClassNotFoundException {
		Class.forName("java.lang.Boolean");
	}
	
	public static void main(String[] args) {
		method1();
		System.out.println("Normal Termination");
	}

}

The compilation error is Unhandled exception type ClassNotFoundException. A method that calls method3() must handle a ClassNotFoundException because there is a throws ClassNotFoundException in method3() method declaration. Place the cursor where the compilation error occurs and click on the second of the solutions Eclipse suggests. Then the source is changed as below and the compilation error disappears.

package net.java_school.exception;

public class Test {

	public static void method1() {
		method2();
	}
	
	public static void method2() {
		try {
			method3();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static void method3() throws ClassNotFoundException {
		Class.forName("java.lang.Boolean");
	}
	
	public static void main(String[] args) {
		method1();
		System.out.println("Normal Termination");
	}

}

Revert the previous code and this time click on the first method that Eclipse suggests. Then, the source is changed as below, and a compilation error occurs on method2(); line in method1() method.

package net.java_school.exception;

public class Test {

	public static void method1() {
		method2();
	}
	
	public static void method2() throws ClassNotFoundException {
		method3();
	}
	
	public static void method3() throws ClassNotFoundException {
		Class.forName("java.lang.Boolean");
	}
	
	public static void main(String[] args) {
		method1();
		System.out.println("Normal Termination");
	}

}

Place the cursor where the compilation error occurs and click on the second of the solutions Eclipse suggests. The code changes as follows.

package net.java_school.exception;

public class Test {

	public static void method1() {
		try {
			method2();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	public static void method2() throws ClassNotFoundException {
		method3();
	}
	
	public static void method3() throws ClassNotFoundException {
		Class.forName("java.lang.Boolean");
	}
	
	public static void main(String[] args) {
		method1();
		System.out.println("Normal Termination");
	}

}

You can see that it terminates normally without any exception.

Normal Termination

After reverting the previous code, place the cursor on the method2(); line in method1() and click on the first method Eclipse suggests. A compilation error occurs in the main method.

package net.java_school.exception;

public class Test {

	public static void method1() throws ClassNotFoundException {
		method2();
	}
	
	public static void method2() throws ClassNotFoundException {
		method3();
	}
	
	public static void method3() throws ClassNotFoundException {
		Class.forName("java.lang.Boolean");
	}
	
	public static void main(String[] args) {
		method1();
		System.out.println("Normal Termination");
	}

}

In the main method, place the cursor where the compilation error occurs and click the first method that Eclipse suggests. The source is changed as follows.

package net.java_school.exception;

public class Test {

	public static void method1() throws ClassNotFoundException {
		method2();
	}
	
	public static void method2() throws ClassNotFoundException {
		method3();
	}
	
	public static void method3() throws ClassNotFoundException {
		Class.forName("java.lang.Boolean");
	}
	
	public static void main(String[] args) throws ClassNotFoundException {
		method1();
		System.out.println("Normal Termination");
	}

}

If you execute it, you can see that it terminates normally without any exception.

Normal Termination

Modify the code so that the exception occurs as follows: The class java.lang.Boolean2 does not exist in the Java API.

package net.java_school.exception;

public class Test {

	public static void method1() throws ClassNotFoundException {
		method2();
	}
	
	public static void method2() throws ClassNotFoundException {
		method3();
	}
	
	public static void method3() throws ClassNotFoundException {
		Class.forName("java.lang.Boolean2");
	}
	
	public static void main(String[] args) throws ClassNotFoundException {
		method1();
		System.out.println("Normal Termination");
	}

}

When executed, the exception object reaches the main method and does not catch an exception in the main method, so the exception object finally reaches the JVM. As a result, the program ends abnormally.

Exception in thread "main" java.lang.ClassNotFoundException: java.lang.Boolean2
        at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:323)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:268)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:190)
        at net.java_school.exception.Test.method3(Test.java:13)
        at net.java_school.exception.Test.method2(Test.java:10)
        at net.java_school.exception.Test.method1(Test.java:6)
        at net.java_school.exception.Test.main(Test.java:18)

Modify the code to catch exceptions in the main method.

package net.java_school.exception;

public class Test {

	public static void method1() throws ClassNotFoundException {
		method2();
	}
	
	public static void method2() throws ClassNotFoundException {
		method3();
	}
	
	public static void method3() throws ClassNotFoundException {
		Class.forName("java.lang.Boolean2");
	}
	
	public static void main(String[] args) {
		try {
			method1();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println("Normal Termination");
	}

}

The e.printStackTrace(); method will print to the console as follows:

java.lang.ClassNotFoundException: java.lang.Boolean2
        at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:323)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:268)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:190)
        at net.java_school.exception.Test.method3(Test.java:14)
        at net.java_school.exception.Test.method2(Test.java:10)
        at net.java_school.exception.Test.method1(Test.java:6)
        at net.java_school.exception.Test.main(Test.java:19)
Normal Termination

This is the same as the output message when the exception object reaches the JVM, but it is not an abnormal termination.

If you remove the catch block from the main method, you will get a compile error. ClassNotFoundException is a checked exception and if you do not control the exception at the caller, you will get a compilation error.

A checked exception object can not escape from a method that does not have a throws checked exception class declared in the class declaration.

package net.java_school.exception;

public class Test {

	public static void method1() throws ClassNotFoundException {
		method2();
	}
	
	public static void method2() throws ClassNotFoundException {
		method3();
	}
	
	public static void method3() throws ClassNotFoundException {
		Class.forName("java.lang.Boolean2");
	}
	
	public static void main(String[] args) {
		try {
			method1();
		} finally {
			System.out.println("Executing a finally block");
		}
		System.out.println("Normal Termination");
	}

}

Put the catch block back into your code to avoid compilation errors.

package net.java_school.exception;

public class Test {

	public static void method1() throws ClassNotFoundException {
		method2();
	}
	
	public static void method2() throws ClassNotFoundException {
		method3();
	}
	
	public static void method3() throws ClassNotFoundException {
		Class.forName("java.lang.Boolean2");
	}
	
	public static void main(String[] args) {
		try {
			method1();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} finally {
			System.out.println("Executing a finally block");
		}
		System.out.println("Normal Termination");
	}

}

Because there is no java.lang.Boolean2 class, a ClassNotFoundException exception occurs, e.printStackTrace(); is executed in the catch block, the finally block is executed, and the last line of the main method is executed.

java.lang.ClassNotFoundException: java.lang.Boolean2
        at java.net.URLClassLoader$1.run(URLClassLoader.java:217)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.net.URLClassLoader.findClass(URLClassLoader.java:205)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:323)
        at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:294)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:268)
        at java.lang.Class.forName0(Native Method)
        at java.lang.Class.forName(Class.java:190)
        at net.java_school.exception.Test.method3(Test.java:14)
        at net.java_school.exception.Test.method2(Test.java:10)
        at net.java_school.exception.Test.method1(Test.java:6)
        at net.java_school.exception.Test.main(Test.java:19)
Executing a finally block
Normal Termination

You will often see this type of message printed by e.printStackTrace(); Most errors can be solved by guessing the basic Java syntax. If you can not solve it, then Googleing is the right answer. The ClassNotFoundException object is created when the class loader can not find the class.

Custom Exception

When an exception occurs, the JVM generates an exception object from the exception class of the Java API and throws the exception to the generated code. However, the exception class can be created by the programmer as needed. This is called a "custom exception". The following is an exception class to be used in an exceptional situation where the balance in the bank program is insufficient.3

package net.java_school.bank;

public class InsufficientBalanceException extends Exception {

	public InsufficientBalanceException() {
		super();
	}
	
	public InsufficientBalanceException(String message, Throwable cause,
		boolean enableSuppression, boolean writableStackTrace) {
		super(message, cause, enableSuppression, writableStackTrace);
	}
	
	public InsufficientBalanceException(String message, Throwable cause) {
		super(message, cause);
	}
	
	public InsufficientBalanceException(String message) {
		super(message);
	}
	
	public InsufficientBalanceException(Throwable cause) {
		super(cause);
	}

}

The JVM does not create a user exception object. Therefore, you must explicitly create a custom exception object in code. Here is the code to create an exception object from a custom exception class:

throw new InsufficientBalanceException("There is not enough balance.");

Test

Write a class that executes as shown below.

C:\ Command Prompt
C:\java\Exception\bin>java net.java_school.bank.Test
$ 1 Deposit: java net.java_school.bank.Test d 1
$ 1 Withdrawal: java net.java_school.bank.Test w 1

C:\java\Exception\bin>java net.java_school.bank.Test d 3
You can not deposit more than the maximum balance.
The balance is $ 1.

C:\>java net.java_school.bank.Test w 2
There is not enough balance.
The balance is $ 1.

Complete the main method.

package net.java_school.bank;

public class Test {
	public static void main(String[] args) {
		int MAX_BALANCE = 3; //Maximum balance amount
		int balance = 1; //Initial balance
		int amount = 0; //Deposit amount or amount of withdrawal
		
		if (args.length < 2) {
			System.out.println("Deposit $ 1: java net.java_school.bank.Test d 1");
			System.out.println("Withdrawal $ 1: java net.java_school.bank.Test w 1");
			return;
		}
		//TODO
	}
}        

Create and apply the appropriate custom exception class in the code above.

Comments
  1. A block is a unit of code that begins with {and ends with}. In Java, variables declared in a block are valid only within blocks.
  2. RuntimeException and its subclasses are called unchecked exceptions, and other exceptions are called checked exceptions.
  3. You can easily create a custom exception class, InsufficientBalanceException, by using the code assist feature in Eclipse. After creating the package and class declaration as above, place the cursor in the class body, right-click and select Source, Generate constructor from superclass..., and you can get the above code.
References