preface

We all know the try catch Finally syntax that fast finally code must execute, but what about the underlying principle? Why do Finnaly syntax blocks execute both normally and when exceptions are thrown


Try catch finally

Let’s start with a simple example:

/ / Java code
public static void main(String[] args) {
    try {
        int a = 1/1;// int a = 1/0
        System.out.println("Normal execution");
    }catch (Exception e){
        System.out.println("Exception");
    }finally {
        System.out.println("Here it is executed."); }}Copy the code

The code above is simple; when int A = 1/1 it prints “normally executed”, “executed here”, and the try and finally blocks execute

When int a = 1/0 outputs “Exception”, “executed here”, and blocks of catch and finally are executed

Now let’s look at bytecode

Int a = 1/1 bytecode
public static void main(java.lang.String[]);
    Code:
       // Lines 0-9 execute the try code block
       0: iconst_1
       1: istore_1
       2: getstatic     #2   // Field java/lang/System.out:Ljava/io/PrintStream;
       5: ldc           #3   // String Executes normally
       7: invokevirtual #4   // Method java/io/PrintStream.println:(Ljava/lang/String;) V
      // finally block is executed here, then jump to line 52 return
      10: getstatic     #2   // Field java/lang/System.out:Ljava/io/PrintStream;
      13: ldc           #5   // String is executed here
      15: invokevirtual #4  // Method java/io/PrintStream.println:(Ljava/lang/String;) V
      18: goto          52
      If an exception is thrown on lines 0-9, the exception will jump to line 21
      // Store thrown Exception output "Exception"
      21: astore_1
      22: getstatic     #2  // Field java/lang/System.out:Ljava/io/PrintStream;
      25: ldc           #7  // String Exception
      27: invokevirtual #4  // Method java/io/PrintStream.println:(Ljava/lang/String;) V
      //30-38 Here is a finally block and then jumps to line 52 return
      30: getstatic     #2  // Field java/lang/System.out:Ljava/io/PrintStream;
      33: ldc           #5  // String is executed here
      35: invokevirtual #4  // Method java/io/PrintStream.println:(Ljava/lang/String;) V
      38: goto          52
      // Lines 2 and 3 of the Exception table, i.e., lines 0-9 throw non-exception exceptions, or lines 21-29 throw any exceptions and jump to line 41
      41: astore_2      // Store thrown exceptions to local variable tables
      // Print "executed here"
      42: getstatic     #2  // Field java/lang/System.out:Ljava/io/PrintStream;
      45: ldc           #5  // String is executed here
      47: invokevirtual #4  // Method java/io/PrintStream.println:(Ljava/lang/String;) V
      50: aload_2  // Take the exception and throw it
      51: athrow
      52: return
    Exception table:
       from    to  target type
           0    10    21   Class java/lang/Exception
           0    10    41   any
          21    30    41   any
Copy the code

Exception table (from, to, target, type

Meaning of each line: from/to indicates the number of lines in the interval, from includes, to does not include; Target and type indicate the number of lines where target is executed if the exception type is type

Take the first line as an example, that is, lines 0 to 9 (inclusive) of the bytecode. If there is an Exception during execution and the type is Java /lang/Exception, the execution jumps to line 21. Any indicates exceptions other than Exception.

After the bytecode analysis above (see the comments), try Catch Finally generates an exception table to aid code execution, and to ensure that finally must be executed, a copy of the finally block is copied within the Try catch block.

Try catch finnaly

Try-catch-finnaly ensures that a finally block will execute. If there is a return value, what will it return?

Let’s look at the sample code:

public static int testReturn(a){
    try {
        int a = 1/0; //int a = 1/1;
        return 0;
    }catch (Exception e){
        return 1;
    }finally {
        return 2; }}public static void main(String[] args) {
    System.out.println(testReturn());
}
Copy the code

The test result of the above code is 2 regardless of whether an exception is thrown.

So let’s look at the bytecode and just look at the testReturn:

  public static int testReturn(a);
    Code:
       // Add finnaly code block to the try block
       0: iconst_1   // Load 1 onto the operand stack
       1: iconst_0   // load 0 onto the operand stack
       2: idiv       / / 1/0
       3: istore_0   // Store the calculated results
       4: iconst_0   // Load 0 to the top of the stack
       5: istore_1   // Store 0 in local variable table but not return
       6: iconst_2   // Load 2 to the top of the stack
       7: ireturn    / / return
       // Add a finally block to the catch block
       8: astore_0   // Store the exception thrown
       9: iconst_1   // Load 1 to the top of the stack
      10: istore_1   // Stores 1 in the local variable table but does not return
        / / 11-12
      11: iconst_2   // Load 2 to the top of the stack
      12: ireturn    / / return 2
        / / finally block
      13: astore_2
      14: iconst_2
      15: ireturn
    Exception table:
       from    to  target type
           0     6     8   Class java/lang/Exception  // If there is an exception, skip to line 8
           0     6    13   any          // If there are other exceptions, go to line 13
           8    11    13   any          // If there are other exceptions, skip to line 13
Copy the code

In order to ensure that a finally block is executed, a try-catch returns only the local variable table, and the finally block copied to each block is returned. So all returns are 2.(Think about how the finally block might not execute if it doesn’t return 2.)

Use caution if there is a return value in try-catch-finally, because finally must be executed. If finally has a return value, the return value of finally must be executed. The return statement in finally may not be written if the return values in try and catch are required.

Try –with–resource

Try-with-resources is a new exception handling mechanism in JDK 7 that makes it easy to close resources used in try-catch blocks. A resource is an object that must be closed after the program is complete. The try-with-resources statement ensures that each resource is closed at the end of the statement. All objects implementing the java.lang.AutoCloseable interface (all objects implementing java.io.Closeable) can use the above usage. Is finally implemented to ensure that resource objects are always closed?

Take a look at the following example:

void readFile(a) {

    try (
            FileReader fr = new FileReader("d:/input.txt");
            BufferedReader br = new BufferedReader(fr)
    ) {
        String s = "";
        while((s = br.readLine()) ! =null) { System.out.println(s); }}catch(Exception e) { e.printStackTrace(); }}Copy the code

Take a look at the bytecode directly:

 void readFile(a); Code: / * Generates the FileReader and places it in the local variable table position1* willnullPlace the local variometer in position2* Generates BufferedReader into local variable table location3* willnullPlace the local variometer in position4* Place the string s initial value "" into the local variable table position5* /0: new           #2   // class java/io/FileReader
       3: dup
       4: ldc           #3  // String d:/input.txt
       6: invokespecial #4  // Method java/io/FileReader."
      
       ":(Ljava/lang/String;) V
      
       9: astore_1
      10: aconst_null
      11: astore_2
      12: new           #5 // class java/io/BufferedReader
      15: dup
      16: aload_1
      17: invokespecial #6 // Method java/io/BufferedReader."
      
       ":(Ljava/io/Reader;) V
      
      20: astore_3
      21: aconst_null
      22: astore        4
      24: ldc           #7 // String
      26: astore        5
      
      /* * Load BufferedReader and call readLine to assign s **/
      28: aload_3
      29: invokevirtual #8 // Method java/io/BufferedReader.readLine:()Ljava/lang/String;
      32: dup
      33: astore        5
      If s is null, the command continues to line 28. If s is null, the command continues to line 49
      35: ifnull        49
      38: getstatic     #9 // Field java/lang/System.out:Ljava/io/PrintStream;
      41: aload         5
      43: invokevirtual #10 // Method java/io/PrintStream.println:(Ljava/lang/String;) V
      46: goto          28
        
      /* * If BufferedReader is null if BufferedReader is null if BufferedReader is null if BufferedReader is null if BufferedReader is null Otherwise, BufferedReader close jumps to line 130 */
      49: aload_3
      50: ifnull        130
      53: aload         4
      55: ifnull        77
      58: aload_3
      59: invokevirtual #11  // Method java/io/BufferedReader.close:()V
      62: goto          130
   
      /** * Execute lines 58-61, BufferedReader. Close if an exception is thrown, it will enter this line * store the thrown exception to local variable table position 5 where the original s is no longer used and overwrites the exception * Fetch the exception at variable table position 4 and 5, Call Throwable. AddSuppressed to join an exception **/
      65: astore        5  
      67: aload         4
      69: aload         5
      71: invokevirtual #13 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;) V
      74: goto          130
        
      // Call BufferedReader close to jump to line 130
      77: aload_3
      78: invokevirtual #11 // Method java/io/BufferedReader.close:()V
      81: goto          130
        
      /** * If the Throwable exception is thrown, the exception will be stored in the local variable table position 5, where the original position s is no longer used and the exception is overwritten by the exception position *. Call Throwable. AddSuppressed to join an exception **/
        
      84: astore        5
      86: aload         5
      88: astore        4
      90: aload         5
      92: athrow
      /* * Store non-throwable exceptions thrown by 24-48 84-95 try calling bufferedReader.close */
      93: astore        6
      95: aload_3
      96: ifnull        127
      99: aload         4
     101: ifnull        123
     104: aload_3
     105: invokevirtual #11 // Method java/io/BufferedReader.close:()V
     108: goto          127
       
     /* * Store exceptions thrown by 104-108 bufferedReader. close and concatenate exceptions */
     111: astore        7
     113: aload         4
     115: aload         7
     117: invokevirtual #13 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;) V
     120: goto          127
       
   
     123: aload_3
     124: invokevirtual #11 // Method java/io/BufferedReader.close:()V
       
     // Throw an exception
     127: aload         6
     129: athrow
       
     /* * check whether FileReader is null if null jump to line 201, not null Otherwise, BufferedReader close jumps to line 201 */
     130: aload_1
     131: ifnull        201
     134: aload_2
     135: ifnull        154
     138: aload_1
     139: invokevirtual #14 // Method java/io/FileReader.close:()V
     142: goto          201
       
     /* * Store exceptions thrown by 138-144 filereader. close and concatenate exceptions */
     145: astore_3
     146: aload_2
     147: aload_3
     148: invokevirtual #13 // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;) V
     151: goto          201
       
     
     154: aload_1
     155: invokevirtual #14 // Method java/io/FileReader.close:()V
     158: goto          201
       
      /* * Stores Throwable exceptions thrown on lines 12-129 */
     161: astore_3
     162: aload_3
     163: astore_2
     164: aload_3
     165: athrow
       
      /* * Stores non-throwable exceptions thrown on lines 12-129 */
     166: astore        8
     168: aload_1
     169: ifnull        198
     172: aload_2
     173: ifnull        194
     176: aload_1
     177: invokevirtual #14 // Method java/io/FileReader.close:()V
     180: goto          198
       
       
     183: astore        9
     185: aload_2
     186: aload         9
     188: invokevirtual #13  // Method java/lang/Throwable.addSuppressed:(Ljava/lang/Throwable;) V
     191: goto          198
       
       
     194: aload_1
     195: invokevirtual #14 // Method java/io/FileReader.close:()V
     
     198: aload         8
     200: athrow
       
     /* * jump to line 209 */
     201: goto          209
       
    /* * Only the last row of the exception table, 0-200, will throw an exception into this area, uncaught exception output */
     204: astore_1
     205: aload_1
     206: invokevirtual #16  // Method java/lang/Exception.printStackTrace:()V
     
     /* * No further action is required. */
     209: return
    Exception table:
       from    to  target type
          58    62    65   Class java/lang/Throwable
          24    49    84   Class java/lang/Throwable
          24    49    93   any
         104   108   111   Class java/lang/Throwable
          84    95    93   any
         138   142   145   Class java/lang/Throwable
          12   130   161   Class java/lang/Throwable
          12   130   166   any
         176   180   183   Class java/lang/Throwable
         161   168   166   any
           0   201   204   Class java/lang/Exception
Copy the code

Such a long bytecode looks silly, don’t panic we have a strategic analysis.

Strategy 1: We divide the bytecode into blocks based on the number of target lines in the exception table
Strategy 2: Block the bytecode for if and goto jumps

And when we’re done with the pieces we’re going to look at each piece and analyze each piece

Normal execution flow:

If s is null, enter line 49(50)

49(50) Check that BufferedReader is not null and there is no exception, execute close to line 130

130-139 Filereader is not null and there is no exception execute close to line 201 and enter 209 end

Abnormal execution process:

Let’s pick a flow. Let’s say 24-49 throws a Throwable. Let’s look at the flow:

The code will go to line 84

84-108: Handle the exception, if BufferedReader is not null, execute bufferedReader.close and enter line 127

127-142: Handle the exception and execute Filereader. close on line 201 and end on line 209

Summary:
  • First, the try-with-resource syntax is not simply added to finallyclosable.close()Method, because the close method in finally will drown out real exceptions if it throws an exception;
  • Secondly, the concept of suppressed exceptions is introduced, where real exceptions can be thrown and addSuppressed is called with suppressed