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 finally
closable.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