First, let’s take a look at the inheritance relationship of exception-related classes in Java:
1. Classification
Exceptions can be divided into checked exceptions and unchecked exceptions. Error and RuntimeException and all their subclasses are unchecked exceptions, and the others are checked exceptions.
The main differences between the two are:
- Checked exceptions are raised by the compiler (Compilation phase) is enforced and must be captured by try-catch or thrown up to indicate an abnormal situation that is not controlled by the program (for example, I/O error).
- Instead of the checked exception being inRuntimeOccurs to indicate a programming error (for example, a null pointer). Because of this, checked exceptions require more code to avoid compilation errors than unchecked exceptions.
Common exceptions include:
The reason why checked exceptions and unchecked exceptions are defined is mainly because they have different functions.
-
In the program, there are some problems that need to be checked by the user during compilation, such as FileNotFoundException and IOException. These exceptions involve resource processing and the caller needs to catch them. In fact, it can remind the developer if such exceptions occur in the called method. , the program should predict and process, such as IOExcetion, we need to close the stream.
-
Unchecked exceptions occur during running and are the types of errors that may occur during program running, such as NullpointExcetpion. We can catch these exceptions or not. But catching these exceptions can only print some logs and nothing else. (Even if it is captured, it cannot be solved in the program)
2. finally and return
2.1 finally principle
public class Demo3_11_4 {
public static void main(String[] args) {
int i = 0;
try {
i = 10;
} catch (Exception e) {
i = 20;//异常表中有3行,分别是监测try中出现的异常、catch中出现的异常(catch中处理异常时也是可能产生异常的)
} finally {
i = 30;
}
System.out.println(i);
}
}
//输出:30
Bytecode:
You can see that the code in finally has been copied 3 times in the bytecode and placed into the try process, the catch process and the catch remaining exception type process. When an exception occurs in the code in the catch, the exception object will be thrown.
2.2 Finally’s impact on return value
- finally return
public class Demo3_12_2 {
public static void main(String[] args) {
int result = test();
System.out.println(result);
}
public static int test() {
try {
return 10;
} finally {
return 20;
}
}
}
//输出 20
- bytecode
//字节码指令
public static int test();
descriptor: ()I
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=1, locals=2, args_size=0
0: bipush 10 // <- 10 放入栈顶
2: istore_0 // 10 -> slot 0 (从栈顶移除了,后面还有字节码,不能return)
3: bipush 20 // <- 20 放入栈顶
5: ireturn // 返回栈顶 int(20)
6: astore_1 // catch any -> slot 1
7: bipush 20 // <- 20 放入栈顶
9: ireturn // 返回栈顶 int(20)
Exception table:
from to target type
0 3 6 any
LineNumberTable: ...
StackMapTable: ...
Since ireturn in finally is inserted into all possible processes, the return result must be based on finally.
- Return in try, modify the return value in finally
public class Demo3_12_2 {
public static void main(String[] args) {
int result = test();
System.out.println(result);
}
public static int test() {
int i = 10;
try {
return i;
} finally {
i = 20;
}
}
}
//输出10
- Bytecode:
You can see that i=10 has been temporarily stored when return is executed in try. Subsequent modification of i will not change i in the local variable table.
2.3 The impact of return in finally on exceptions
We can see in 2.1 that in order to prevent exceptions from being thrown directly during catch exception processing without executing the code in finally, in the bytecode, the code in try is also tracked for exceptions, and Finally thrown out.
There is the following code:
public class Demo3_12_2 {
public static void main(String[] args) {
int result = test();
System.out.println(result);
}
public static int test() {
int i = 10;
try {
i = 1/0;
return i;
} finally {
i = 20;
//return i;
}
}
}
//输出:30
- Question 1: Will an exception be thrown?
public static int test();
descriptor: ()I
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=0
0: bipush 10
2: istore_0
3: iconst_1
4: iconst_0
5: idiv
6: istore_0
7: iload_0
8: istore_1
9: bipush 20 -----------
11: istore_0
12: iload_1
13: ireturn -----------
14: astore_2
15: bipush 20 -----------
17: istore_0
18: aload_2
19: athrow
Exception table:
from to target type
3 9 14 any
As can be seen from the bytecode, the current code is in try - bytecodes 3 to 9 (excluding 9). If an exception occurs, it will jump directly to 14, put the exception object in slot2, and then execute the finally code to change 20 ->i, throws an exception object. Therefore, an exception can be thrown.
- Question 2: Will an exception be thrown after adding return in finally?
public static int test();
descriptor: ()I
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=3, args_size=0
0: bipush 10
2: istore_0
3: iconst_1
4: iconst_0
5: idiv
6: istore_0
7: iload_0
8: istore_1
9: bipush 20
11: istore_0
12: iload_0
13: ireturn
14: astore_2
15: bipush 20
17: istore_0
18: iload_0
19: ireturn
Exception table:
from to target type
3 9 14 any
It can be seen that after adding return in the code finally, the throw instruction in the bytecode is gone, which means that the exception will not be thrown, that is, the return in finally will swallow the exception object, so do not return in finally.
Summary: The reason why finally swallows exceptions is to ensure that the code in finally must be executed before an exception is thrown. However, if there is a return in finally, the execution of the return method in finally will end, and there will be no chance to run the exception.