The labs on this site need JavaScript to run. The same content is written out below.
Fundamentals › Java
Exceptions
What is an exception, and what does try-catch do?
An exception is an object that Java throws when something happens while the program is running and it cannot continue. For example, when you divide an integer by 0, Java throws an exception called ArithmeticException, and at the moment it is thrown, the program stops what it was doing right there. If nobody catches it, the program ends there and the lines after it do not run. try { } wraps code that might fail. The catch (kind of exception e) { } after it is the place where execution jumps when that kind of exception occurs, and handles it. When the handling is done, the program keeps running the lines below the try-catch. In this lab you compile and run the same code while you remove the try-catch, change the value of b, and change the kind that catch catches, and you see what is printed and how the program ends.
Checked against real execution · Eight combinations were compiled with javax.tools on JDK 21 and run as child java processes, and the standard output, standard error, and exit code were checked
Concepts in this lab
- Exception and Error
- Both are subtypes of Throwable that represent abnormal situations while a program runs. An Exception is a situation that the application can catch and handle (such as invalid input or a missing file). An Error is a serious problem at the JVM level, such as running out of memory or a stack overflow, and applications usually do not try to recover from it.
- try-catch
- try-catch is a statement that decides where execution goes to handle an exception when one occurs. You wrap code that might fail in try { } and add catch (kind of exception e) { } after it. When an exception is thrown inside try, execution stops there. If the kind of the thrown exception is the kind written in catch or a subkind of it, execution jumps into catch, runs it, and then continues with the lines below the try-catch. If there are several catch clauses, the first one that matches, from the top, is used. If no catch matches, the exception keeps passing outward past the try-catch, and if nobody catches it, that thread ends. If no exception occurs, the code inside catch does not run.
- Throwing an exception (throw)
- Throwing an exception means creating an exception object, stopping what is being done now, and moving execution to the place that catches it. When something breaks the rules of Java, such as dividing an integer by 0, Java throws an exception by itself, and your code can also throw one directly with a throw statement, as in throw new IllegalStateException(). After an exception is thrown, execution does not go back to the place where it was thrown and continue. Java goes up through the callers one by one looking for a try-catch that can catch this exception, and if there is none all the way up, that thread ends.
- NullPointerException
- This exception is thrown when code tries to call a method or read a field through a reference that is null. null means that there is no object that the reference refers to, and an object that does not exist cannot be asked to do anything, so the exception is thrown. Whether a value is null depends on the values at run time, so the compiler cannot catch it in advance and it shows up only when the code runs (it is an unchecked exception, so handling is not forced either). This exception is also thrown when null is passed to a method that is written not to accept null (such as Optional.of or Objects.requireNonNull). In places where a value may be absent, you make this exception less likely by checking for null first or by using Optional to show in the type that the value may be absent.
Try these
- Default settings — The moment you divide by 0, an exception is thrown, catch receives and handles it, and the program runs on to "Done".
- Remove try-catch — Nothing catches the exception, so the program stops at that line and "Done" is not printed.
- Store 2 in b — No exception occurs, so 5 and "Done" are printed, and the code inside catch does not run.
- Catch a different kind — catch cannot catch ArithmeticException, so the program stops.
Running with the default settings
- You run it with java Main. The main method starts and the lines int a = 10; and int b = 0; run in order, so the value stored in variable a is 10 and the value stored in variable b is 0.
- Execution reaches the try { line. The lines inside try run from the top as usual. try tells Java in advance, "if an exception occurs in here, pass it to the catch below." If no exception occurs, the code runs the same as if try were not there.
- The line System.out.println(a / b); runs. To print, println must first calculate a / b inside the parentheses. At the moment it tries to calculate 10 / 0, Java (the JVM) sees that an integer cannot be divided by 0, creates an exception object called ArithmeticException, and throws it. When an exception is thrown, Java stops what it was doing right there, so println, which has no value to print, does not run.
- Java looks for a place that catches this exception. The exception occurred inside try, so it looks at the catch attached to this try. catch (ArithmeticException e) says it catches the ArithmeticException kind, and the exception that was thrown is an ArithmeticException, so the kind matches.
- Execution jumps into catch. The thrown exception object is stored in variable e, and the line System.out.println("Cannot divide by zero"); inside catch runs and prints that text.
- When the lines inside catch finish, the exception counts as handled. The program does not stop. It continues with the lines below the try-catch, and the line System.out.println("Done"); prints "Done". The last line of main has run, so the program ends.
Result of this run
- An exception is an object that Java throws when something happens at run time and it cannot continue. ArithmeticException was thrown at 10 / 0, so println(a / b) did not run, but the code was wrapped in try-catch, so execution jumped into catch and handled it, and then ran on to "Done" below the try-catch.
- An exception does not end the program right away. If there is a catch with a matching kind, it receives the exception, handles it, and execution continues. The code does not go back to the place where the exception was thrown, so the rest of the line where the exception occurred does not run.
Code
public class Main {
public static void main(String[] args) {
int a = 10;
int b = 0;
// try: wrap code that might fail
try {
System.out.println(a / b);
// catch: if an exception occurs, execution jumps here
} catch (ArithmeticException e) {
System.out.println("Cannot divide by zero");
}
System.out.println("Done");
}
}
Assumptions of this model
The compiler and runtime are JDK 21.0.12.1 (javac 21). For all eight combinations, the source that is the same as the code on the screen (identical text, line by line) was compiled with javax.tools, and all of them compiled. The compiled Main was then run by starting this JDK's java as a child process, and the results were checked against the standard output, standard error, and exit code that were captured. What was checked is whether compilation succeeded, the lines of standard output (the Output column of the result table), the exit code and the first line of standard error (the How the program ended column: when it ended normally, the exit code was 0 and standard error was empty; when it stopped, the exit code was 1, the first line was Exception in thread "main" java.lang.ArithmeticException: / by zero, and a stack trace containing Main.main followed in standard error), and whether the code inside catch ran (whether the text printed by the println inside catch is in standard output). Every combination that stopped had empty standard output, so "Done" was not printed. In the combinations without try-catch, the setting for the kind that catch catches is not used, so the two combinations give the same result. The documentation was checked by opening the Java Language Specification (Java SE 21) directly. That the Java virtual machine signals a violation of the rules as an exception and control is transferred to a point specified by the program is from the introduction of Chapter 11; that when an exception is thrown, control is transferred to a catch that can handle it in the nearest enclosing try, the code that caused the exception is not executed again, and the thread ends if there is no catch to handle it is from Section 11.3; that throw transfers control immediately and the thread ends if there is no matching try is from Section 14.18; that a catch has exactly one parameter and its type must be Throwable or a subclass of it is from Section 14.20; that if the kind of the thrown value can be assigned to the kind written in a catch, the leftmost matching catch is selected and its block runs, that the try statement also completes normally if that block completes normally, and that the try statement ends with the same exception if there is no matching catch is from Section 14.20.1; and that an ArithmeticException is thrown when the divisor of an integer division is 0 is from Section 15.17.2. The matching kinds in Section 14.20.1 also include subkinds of the written kind, but the catch clauses in this lab are only ArithmeticException and NullPointerException, and the two are not in a superclass-subclass relationship, so the case of catching a subkind is not covered. The text in standard error and the exit code 1 are not defined by the specification; they were observed on this java. The code on the screen has the same form as running with java Main, and there is no package. The 10 / 0 and 10 / 2 in the diagram spell out the values stored in a and b. Exceptions also involve using several catch clauses, finally, throwing directly with throw, looking for a place to catch by going back up through the methods that were called, and checked exceptions, but this lab does not cover them. They are covered in the in-depth lab "Exceptions and finally".
Previous lab: Interfaces · Next lab: Generics