Exception
Java API has exception classes for every exception while using Java API. If an exception occurs during execution, JVM creates an object from the corresponding exception class and forces the code that has caused the exception to return that. Programmers can use try, catch, and finally blocks appropriately to control the exception objects that JVM may raise.
- block
-
A block is a unit of source code that begin with { and ends with }.
In Java, variables declared in a block are valid only within the block.
What are errors in Java?
Exceptions are strictly errors, but Java distinguishes between exceptions and errors. An error in Java is an error that you can not control programmatically. If an error occurs, programs terminate.
Control Exceptions
Exception classes have a hierarchical structure.
The Exception is 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 }
If the programmer has not taken appropriate precautions, an exception will cause the program to terminate.
Proper precautions include:
- Put the code that can throw an exception within a try block.
- Use the catch block to catch exceptions raised by a try block. --Caught exceptions cannot escape the catch block unless you rethrow the exception using the throw keyword --
- A finally block is optional when there is at least one catch block. Note that JVM always executes the code in this block whether or not an exception happens.
- try block
-
It is a block that encloses code that can cause exceptions.
You can not use it alone, and use it with catch blocks and a finally block. - catch block
- To handle an exception that may occur in a try block, you must put a catch block after the try block that catches that exception. You can fine-tune exceptions using multiple catch blocks, as in the example above.
- finally block
- Whether an exception occurs or not, JVM executes code in this block. You cannot use this block multiple times in a row like a catch block.
Exception mechanism
Assume that method1 calls method2 and method2 calls method3, as shown below.
If an exception occurs in method3 and a catch block catches the exception in method3 body, the program continues without stopping.
If there is no catch block or the exception thrown in the body of method3 is not caught, the exception goes to the code of method2 that called method3.
Assume that method2 does not have a catch block that catches the exception thrown by method3. This exception goes to the code of method1 that called method2.
When an exception reaches JVM, JVM terminates that program. Such termination is an abnormal termination.
Exception class hierarchy
Frequent 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 SomeException in the method declaration
After the throws keyword, you can put an exception class or a comma-separated list of exception classes in the method declaration. A compilation error will occur if the code that calls this method does not have a code snippet that correctly handles exceptions defined after the throws keyword.
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 must 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:
This method does not catch the SomeException type exception inside the method but throws it out of the method. If SomeException is a RuntimeException or a subclass of RuntimeException, You can omit "throws SomeException" from the method declaration section.
2:
This method catches and handles the exception that occurred inside the method. If SomeException is a RuntimeException or a subclass of RuntimeException, there is no compilation error even if you omit the try and catch block. In this case, the exception escapes the method.
The code that handles the exception is one of the following:
- Let the exception escape
- Catch and handle the exception
Examples
The following is an example of an unchecked exception. Note: Unchecked exceptions are RuntimeException or subclass of RuntimeException. These exceptions do not cause a compile error even if you do not code that handles them.
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 instance reaches JVM. JVM sequentially prints the stack on which the exception occurred, as shown below. The important thing is that it is not a normal Termination. If executed to the last line, the main method will print "Normal Termination."
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)
- Stack
- A Stack is a data structure, and the implementation of a stack is also called a LIFO (Last In First Out).
- When a code calls a method, JVM creates memory space for executing it and stacks the room on the stack.
- The stack is a space where local variables are stored. Local variables are variables declared in the method body. When the method ends, the room occupied for executing it on the stack vanishes. At this point, the method's local variables also vanish.
The following figure shows memory spaces accumulated on the stack according to methods execution order and messages that JVM traces and outputs the exception cause.
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()) code prints / by zero.
/ by zero Normal Termination
Revert 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 instance from method3() goes 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 instance goes to method1(), and method1() adequately handles it, thus avoiding abnormal termination.
/ by zero Normal Termination
Revert the previous code and modify the method main 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 instance goes to the method main. The method main handles the exception instance correctly, so we avoid the program terminating abnormally.
/ by zero Normal Termination
A try block does not necessarily have to be with a catch block. However, if there is no catch block, you can not catch an exception object. The following example removes the catch block. You cannot use a try block alone, so add 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 instance reaches JVM, and the program terminates abnormally. You can confirm that JVM executed the finally block, and the exception instance went to JVM through the output.
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 to catch every 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, JVM executed the catch block and the finally block. The exception object caught by the catch block is no longer passed anywhere.
/ by zero Executing a finally block Normal Termination
You can write catch blocks consecutively like if ~ else if ~ else if. But you need to pay attention to the hierarchical relationship of exception classes.
When an exception occurs in a try block, catch blocks try 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 chance 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 catch blocks, 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 the program runs, the catch(ArithmeticException e) {} block catches the exception thrown by the try block. JVM executes e.getMessage() in this block and executes the code of the finally block, followed by the last line of the main.
/ by zero Executing a finally block Normal Termination
Modify the above 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.
The java.lang.Class belongs to 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, the JVM creates a ClassNotFoundException exception instance and throws it to the Class.forName ("string") line.
In the example, the class corresponding to "java.lang.Boolean" passed as an argument to forName() belongs to Java API, so the class loader looks for this class without the cp or classpath option.
There is "throws ClassNotFoundException" in the method declaration of the forName() method. Because ClassNotFoundException is a checked exception that does not inherit RuntimeException, the code calls forName() must handle this exception.
Let's modify the code using the code assist feature of Eclipse. If you put your cursor on the code: Class.forName(), Eclipse will give you two solutions. If you choose the second one, 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"); } }
Since the Boolean (java.lang.Boolean) exists in Java API, JVM does not execute the catch block.
Normal Termination
Revert the previous code and choose the first Eclipse suggests.
The method3() line in method2() 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() 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 "throws ClassNotFoundException" in method3() method declaration.
Place the cursor where the compilation error occurs and choose 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 choose the first solution that Eclipse suggests. Then, the source is changed below, and a compilation error occurs on the method2() line in the 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 choose the second of the solutions Eclipse suggests. Then the source is changed as below.
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 the program terminates without any exception.
Normal Termination
Revert the previous code, place the cursor on the method2() line in the method1(), and choose the first solution Eclipse suggests. A compilation error occurs on 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"); } }
Place the cursor where the compilation error occurs and choose the first solution that Eclipse suggests. Then 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"); } }
You can see that the program terminates without any exception.
Normal Termination
Let's change "java.lang.Boolean" to "java.lang.Boolean2" in the code.
Note:
The java.lang.Boolean2 does not belong to 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 instance reaches the method main, and the method does not catch the exception, so the exception instance finally goes to 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 the exception in the method main.
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() 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
The above looks like the output message when the exception instance reaches the JVM, but it is not an abnormal termination.
If you remove the catch block from the method main, 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 cannot escape from a method that does not have a declaration that throws the exception 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"); } }
Add the catch block back to avoid a compilation error.
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"); } }
The class java.lang.Boolean2 is not in Java API, and we didn't create it, so a ClassNotFoundException occurs. JVM executes the e.printStackTrace() statement of the catch block, followed by the finally block's code, followed by the last line of the method 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: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 message printed by the e.printStackTrace() statement. You may solve most errors by guessing the basic Java syntax, but Googling may be the solution if you can't.
Custom Exceptions
When an exception occurs, JVM generates an exception instance from exception classes of Java API and throws the exception to the code.
Programmers can create exception classes to suit their needs. These are called Custom Exceptions.
Here is an exception class to use when the bank account's balance is insufficient.
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); } }
JVM does not generate this exception instance when the bank account's balance is insufficient. Therefore, we must explicitly create our custom exception instance in code.
Here is the code to create an exception instance from our custom exception class:
throw new InsufficientBalanceException("There is not enough balance.");
Test
Write a class that executes as shown below.
C:\ Command PromptC:\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 method main.
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 on the above code.
References