Java exception classification and the impact of return statements in finally code blocks

First, let’s take a look at the inheritance relationship of exception-related classes in Java:

Quote
Insert image description here

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:
Insert image description here
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:
Insert image description here
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

  1. 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.

  1. 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:
    Insert image description here
    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.

Guess you like

Origin blog.csdn.net/baidu_40120883/article/details/129291025