Can not underestimate this simple finally, seemingly simple problems behind, but hidden countless mystery. Next I will take you step-by-step to uncover this finally mysterious veil.
Problem Analysis
first of all, let me ask you a question: Finally statement blocks are bound to execute?
Many people think that finally statement blocks are definitely going to execute, including some very experienced Java programmers. Unfortunately, as most people think, the answer to this question is, of course, negative, let's look at the following example.
Listing 1.
public class Test {public
static void Main (string[] args) {
System.out.println (' return value of test (): "+ Test ( ));
}
public static int Test () {
int i = 1; if (i = = 1)
//return 0;
System.out.println ("the previous statement of Try Block");
i = i/0;
try {
System.out.println ("try block");
return i;
} finally {
System.out.println ("finally Block");}}
The results of Listing 1 are as follows:
The previous statement of Try block
Exception in thread ' main ' java.lang.ArithmeticException:/by zero at
COM.BJ. Charlie. Test.test (test.java:15) at
Com.bj.charlie.Test.main (test.java:6)
In addition, if you remove the annotation before the two statement that is commented in the previous example, the result is:
return value of Test (): 0
In both cases, the finally statement block is not executed, what is the problem? A finally statement block is executed only if the block of the finally corresponding try statement is executed. Both of these cases are returned (return) or throw an exception before the try statement block, so the finally statement block for the try is not executed.
Well, is the finally statement block bound to execute even if a block of a finally-corresponding try statement is executed? Sorry, this may let everyone down again, the answer is still negative. Take a look at the following example (Listing 2).
Listing 2.
public class Test {public
static void Main (string[] args) {
System.out.println (' return value of test (): "+ Test ( ));
}
public static int Test () {
int i = 1;
try {
System.out.println ("try block");
System.exit (0);
return i;
} finally {
System.out.println ("finally Block");}}
The results of Listing 2 are as follows:
Finally statement block is still not executed, why? Because we executed the System.exit (0) statement in the TRY statement block, the Java virtual machine was terminated. It was said that the System.exit (0) method was basically not invoked in a typical Java application. Ok! No problem, we do not call System.exit (0) This method, then finally statement block will definitely execute?
Once again let everyone down, the answer is still negative. When a thread is interrupted (interrupted) or terminated (killed) while executing a try statement block or catch statement block, its corresponding finally block of statements may not be executed. The more extreme case is when a thread runs a try statement block or catch statement block, suddenly crashes or loses power, and finally the block is definitely not executed. There may be some people think that the crash, power outages, some of the reasons are not related, we just to illustrate the problem.
Finally statement profiling
Having said so much, let us come up with some convincing evidence. What other evidence is more persuasive than official documents? Let's take a look at how the "Java tutorials" on the official website describes the finally statement block!
The following content, located between * * *, is intact from the Java tutorials document.
*******************************************************************************
The Finally block
The finally block always executes is the try block exits. This ensures to the finally block is executed even if a unexpected exception occurs. But finally is useful for more than just exception handling-it allows the programmer to avoid has cleanup code Accide Ntally bypassed by a return,continue, or break. Putting cleanup code in a finally blocks is always a good practice, even when no exceptions are.
Note:if the JVM exits while the try or catch code is being executed, then the "Finally block" not execute. Likewise, if the thread executing the "Try or catch code is interrupted" or killed, the finally block could not execute even t Hough the application as a whole continues.
*******************************************************************************
Please read carefully and appreciate the above two paragraphs, when you really understand the exact meaning of these two paragraphs, you can be very confident to answer the "finally statement block is sure to execute?" "Such a question. It seems that most of the time, it is not how profound the Java language itself is, but that we overlook the deep understanding of the basics.
Next, let's look at how the finally statement block is executed. After excluding the fact that the above finally statement block does not execute, the finally statement block is guaranteed to execute, and since the finally statement block must be executed, what is the order of execution of it and the try statement block and the catch statement block? Also, if there is a return statement in the TRY statement block, will the finally statement block be executed before return, or should it be executed at the end? With such questions, we will explain them in concrete cases.
Let's take a look at the authoritative discussion about try, catch, and finally the order of execution. The following * * * is excerpted from the description of Try,catch, and finally, from the fourth edition of the Java Language Specification ("The Java™programming Language, Fourth Edition").
*******************************************************************************
12.4. Try, catch, and finally
You catch exceptions by enclosing code in Try blocks. The basic syntax for a Try blocks is:
try {
statements
} catch (Exception_type1 identifier1) {
statements
} catch (Exception_type2 identifier2) {
statements
...
} finally {
statements
}
Where either at least one catch clause, or the FINALLY clause, must is present. The body of the try statement are executed until either an exception are thrown or the body finishes successfully. If A exception is thrown, each catch clause be examined in turn, from the to- The Tion object is assignable to the type declared the catch. When a assignable catch clause is found, the IT block are executed with its identifier set to reference the exception object. No other catch clause would be executed. Any number of catch clauses, including zero, can is associated with a particular TRy as long as each clause catches a diff Erent type of exception. If no appropriate catch is found, the exception percolates out of the the try statement to any outer try that might have a C Atch clause to handle it.
If a finally clause is present with a try, the IT code is executed after all other processing in the try is complete. This is happens no matter how completion is achieved, whether normally, through a exception, or through a control flow stat Ement such as return or break.
*******************************************************************************
The general meaning of the above text is that the finally statement block is guaranteed to execute regardless of whether the try statement block ends normally or abnormally. If the TRY statement block ends normally, then the finally statement block is executed after the statements in the TRY statement block are executed. What if there is a control transfer statement (return, break, continue) in a try? is the finally statement block executed before or after the transfer statement is controlled? It seems that from the above description we can not see any clue, do not worry, we will analyze the problem in the following explanation. If the TRY statement block is terminated abnormally, you should first go to the corresponding catch block to do exception handling, and then execute the FINALLY statement block. The same question, what if the Catch statement block contains a control transfer statement? is the finally statement block executed before or after these control transfer statements? We will also mention it in subsequent discussions.
In fact, the implementation process for try,catch,finally is far from simple, and interested readers can refer to the third edition of the Java Language Specification ("The Java™language specification, Third Edition") for Execution of the description of try-catch-finally, a very complex process. For the reasons of space, this article does not make excerpts, please read by interested readers.
Finally Statement sample description
Now let's look at a simple example (listing 3).
Listing 3.
public class Test {public
static void Main (string[] args) {
try {
System.out.println (' try Block ');
return;
} finally {
System.out.println ("finally Block");}}
The results of Listing 3 are:
Listing 3 shows that the finally statement block executes before the return statement in a try statement block. Let's look at another example (listing 4).
Listing 4.
public class Test {public
static void Main (string[] args) {
System.out.println ("Reture Value of Test ():" + te St ());
}
public static int Test () {
int i = 1;
try {
System.out.println ("try block");
i = 1/0;
return 1;
} catch (Exception e) {
System.out.println ("Exception block");
return 2;
} finally {
System.out.println ("finally Block");}}
The results of Listing 4 are:
Try block
exception blocks
finally
reture value of Test (): 2
Listing 4 shows that the finally statement block executes before the return statement in the CATCH statement block.
From Listing 3 and listing 4 above, we can see that the finally statement block is actually executed before the return statement in a try or catch. More generally, the finally statement block should be executed before the transfer statement is controlled, and the control transfer statement has break and continue in addition to return. In addition, the throw statement is also part of a control transfer statement. Although return, throw, break, and continue are all control transfer statements, there is a difference between them. where return and throw transfer program control to their callers (invoker), while the break and continue control is transferred within the current method. Please remember their differences first, in the follow-up analysis we will talk about.
Or get some convincing evidence, the following excerpt from the fourth edition of the Java Language Specification ("The Java™programming Language, Fourth Edition"), please read the meaning of the reader yourself.
*******************************************************************************
Afinallyclause can also be used to clean up forbreak,continue, Andreturn, which are one reason you'll sometimes the ATRYC Lause with Nocatchclauses. When no control transfer statement are executed, all relevantfinallyclauses are executed. There is no way to leave atryblock without executing.
*******************************************************************************
Well, see here, does anyone think that they have mastered the finally usage? Don't jump to conclusions, let's look at two more examples – Listing 5 and listing 6.
Listing 5.
public class Test {public
static void Main (string[] args) {
System.out.println ("Return value of GetValue ():" + G Etvalue ());
}
public static int GetValue () {
try {return
0;
} finally {return
1;
}
}}
The results of the execution of listing 5:
return value of GetValue (): 1
Listing 6.
public class Test {public
static void Main (string[] args) {
System.out.println ("Return value of GetValue ():" + G Etvalue ());
}
public static int GetValue () {
int i = 1;
Try {return
i;
} finally {
i++
}
}}
The results of the execution of listing 6:
return value of GetValue (): 1
Using our analysis above, we conclude that a finally statement block is executed before the return statement in a try or catch. As a result, it is easy to understand that listing 5 results in the execution of 1. Since finally the return 1 statement is to be executed before the return 0 statement in a try, then the finally returns 1, after the execution of the statement, transfers control of the program to its caller main () function, and the returned value is 1. So why is the return value of listing 6 not 2, but 1? According to the analysis logic in Listing 5, the i++ statement in finally should be executed before return I in a try. The initial value of I is 1, then the execution i++, then 2, then the return I; that's supposed to be 2? How did it become 1?
As for how the Java virtual machine compiles the finally statement block, interested readers can refer to the "the JAVATM Virtual Machine specification, Second Edition" section 7.13 compiling F Inally. There is a detailed description of how the Java virtual machine compiles a finally statement block. In fact, Java virtual opportunity to the finally block as subroutine (for this subroutine do not know how to translate for good, simply do not translate, lest produce ambiguity and misunderstanding. is inserted directly before the control transfer statement of a try statement block or catch statement block. However, there is another factor that cannot be overlooked, that is, before executing subroutine (that is, a finally statement block), a try or a catch statement block retains its return value to the local variable table (locally Variable table). After the subroutine is finished, restore the reserved return value to the operand stack, and return it to the caller (invoker) of the method by either returning or throw statements. Notice that we mentioned the difference between return, throw and break, continue in the previous article, and for this rule (which retains the returned value), it applies only to returns and throw statements, not to break and continue statements, because they simply do not return Value.
is not very good understanding, then we use concrete examples to do the description of the image of it!
To be able to explain the results of listing 6, let's analyze the byte code for listing 6 (Byte-code): Compiled from "Test.java"
public class Test extends java.lang.object{
public Test ();
Code:
0:aload_0
1:invokespecial#1; Method Java/lang/object. " <init> ":() V
4:return
Linenumbertable:
Line 1:0
public static void Main (java.lang.string[]);
Code:
0:getstatic #2; Field Java/lang/system.out:ljava/io/printstream;
3:new #3; Class Java/lang/stringbuilder
6:dup
7:invokespecial #4; Method Java/lang/stringbuilder. " <init> ":() V
10:LDC #5; String return value of GetValue ():
12:invokevirtual
#6; Method Java/lang/stringbuilder.append: (
ljava/lang/string;) Ljava/lang/stringbuilder;
15:invokestatic #7; Method GetValue: () I
18:invokevirtual
#8; Method Java/lang/stringbuilder.append: (I) Ljava/lang/stringbuilder;
21:invokevirtual
#9; Method java/lang/stringbuilder.tostring: () ljava/lang/string;
24:invokevirtual #10; Method Java/io/printstream.println: (ljava/lang/string;) V
27:return
public static int getValue ();
Code:
0: iconst_1
1: ISTORE_0
2: IL Oad_0
3: istore_1
4: iinc 0, 1
7: Iload_ 1
8: Ireturn
9: astore_2
: iinc 0, 1 br> : aload_2
: athrow
Exception table:
fro m to target type
2 4 9 any
9 10 9 any
& nbsp;}
For the Test () construct method and the main () method, we do not explain too much here. Let's analyze the execution of the GetValue () method. Before that, let me explain the virtual machine instructions used in getValue () so that the reader can correctly understand the execution of the function.
1. Iconst_
Description:push the "int constant" ( -1, 0, 1, 2, 3, 4 or 5) onto the operand stack.
FORMS:ICONST_M1 = 2 (0x2) Iconst_0 = 3 (0x3) iconst_1 = 4 (0x4)
Iconst_2 = 5 (0x5) Iconst_3 = 6 (0x6) Iconst_4 = 7 (0x7) Iconst_5 = 8 (0x8)
2. Istore_
Description:store int into the local variable. The must be a index into the
The local variable array is the current frame.
Forms:istore_0 = 0x3b Istore_1 = (0x3c) istore_2 = (0x3d)
Istore_3 = (0x3e)
3. Iload_
Description:load int from the local variable. The must be a index into the
The local variable array is the current frame.
Forms:iload_0 = num (0x1a) iload_1 = (0x1b) iload_2 = (0x1c) iload_3 = MB (0x1d)
4. Iinc Index, const
Description:increment local variable by constant. The index is a unsigned byte
Must be a index into the local variable array of the current frame. The Const is an
Immediate signed byte. The local variable at index must contain a int. The value
The const is a sign-extended to a int, and then the local variable at index is
incremented by that amount.
Forms:iinc = 132 (0x84)
Format:
Iinc
Index
Const
5. Ireturn
Description:return int from method.
Forms:ireturn = 172 (0XAC)
6. Astore_
Description:store reference into the local variable. The must be a index into the
The local variable array is the current frame.
Forms:astore_0 = Astore_1 = 0x4b (0x4c) astore_2 =77 (0x4d) astore_3 =78 (0x4e)
7. Aload_
Description:load reference from the local variable. The must be a index into the
The local variable array is the current frame.
Forms:aload_0 = 0x2a Aload_1 = 0x2b aload_2 = (0x2c) aload_3 = (0x2d)
8. Athrow
Description:throw exception or error.
Forms:athrow = 191 (0XBF)
With the above Java virtual machine directives, let's analyze the order of execution: divided into normal execution (no exception) and exception execution (with exception). Let's take a look at the normal execution, as shown in Figure 1:
Figure 1. GetValue () function Normal execution
From the above, we can see clearly that before the finally statement block (Iinc 0, 1) executes, the GetValue () method holds the position of its return value (1) to 1 of the surface scale, and the instruction to complete the task is istore_1; then execute finally Statement block (Iinc 0, 1, finally statement block to the position at 0 in the local variable table value plus 1, to 2; After finally statement block execution, restore the value of 1 in the local table scale to the operand stack (iload_1), and finally execute the ireturn instruction The value (1) in the current operand stack is returned to its caller (main). That's why listing 6 results in 1, not 2.
Let's take a look at the execution of the exception. Does anyone ask if there is no catch statement in your listing 6, and where is the exception handling? I think this is a good question, in fact, even without a catch statement, the Java compiler compiles a bytecode that has default exception handling, and remember that there may be exceptions (such as RuntimeException and Error) that need to be caught, in addition to the exception that needs to be caught.
From the byte code of the GetValue () method, we can see its Exception handling table (Exception table), as follows:
Exception table: From to
target type
2 4 9 No
It means that if the instruction from 2 to 4 is abnormal, it is handled by the instructions starting from 9.
Figure 2. GetValue () function Exception execution
First of all, the exception in the picture above should be a reference to the exception object, in order to facilitate the explanation, I wrote it directly exception.
The above figure (Figure 2) shows that when an exception occurs from 2 to 4, a exception object is generated and pressed into the top of the stack on the current operand stack. The next step is to astore_2 this instruction, which is responsible for saving the exception object to 2 of the local variable table, and then executing the finally statement block until the end statement block is executed, followed by aload_2 this instruction to put the pre-stored exception object reverts to the operand stack, which is then returned to the caller (main) of the method by the athrow instruction.
Through the above analysis, we should already clear try-catch-finally statement block of the implementation process!
In order to be more persuasive, we still have to quote the Scripture! People can not believe me, do not believe that "high Commander" (Gosling)? The following section is still excerpted from the Java Language Specification Fourth Edition "The Java™programming Language, fourth Edition", please the reader own experience!
*******************************************************************************
A finally clause is always entered with a reason. That is reason May is that the try code finished normally, which it executed a control flow statement such as return, or that An exception is thrown in code executed into the Try block. The reason is remembered then the finally clause exits by falling out the bottom. However, if the finally block creates it own reason to leave by executing a control flow statement (such as break or Retu RN) or by throwing a exception, that reason supersedes the original of one, and the original reason is forgotten. For example, consider the following code:
try {
... do something ...
return 1;
finally {
return 2;
}
When the ' Try block ' executes its return, the finally-entered with the ' reason ' of returning the value 1. However, inside the "finally block" the value 2 is returned, so the initial intention is forgotten. In fact, if any of the "other" code in the try block had thrown a exception, the result would still is to return 2. If the finally block did is not return a value but simply fell out of the bottom, the "Return of the value 1″reason would be reme Mbered and carried out.
*******************************************************************************
Well, with the above knowledge, let's take a look at the following 3 examples.
Listing 7.
public class Test {public
static void Main (string[] args) {
System.out.println ("Return value of GetValue ():" + G Etvalue ());
}
@SuppressWarnings ("finally") public
static int getValue () {
int i = 1;
try {
i = 4;
} finally {
i++;
return i;}}
The results of the execution of listing 7:
return value of GetValue (): 5
Listing 8.
public class Test {public
static void Main (string[] args) {
System.out.println ("Return value of GetValue ():" + GetValue ());
}
public static int GetValue () {
int i = 1;
try {
i = 4;
} finally {
i++;
}
return i;
}
}
The results of the execution of listing 8:
return value of GetValue (): 5
Listing 7 and listing 8 should be relatively simple! Using the knowledge we explained above, it is easy to analyze the results. Let's look at a slightly more complex example-listing 9. I suggest that you should not look at the implementation results, use the knowledge learned to analyze, to see whether the correct results can be inferred.
Listing 9.
public class Test {public
static void Main (string[] args) {
System.out.println (test ());
}
public static String test () {
try {
System.out.println (' try Block ');
return test1 ();
} finally {
System.out.println ("finally Block");
}
public static String Test1 () {
System.out.println (' return statement ');
Return ' after return ';
}
The result of listing 9:
Try block return
statement
finally blocks after return
Did you analyze the right? In fact, this case is not very difficult, return test1 (), this statement is equivalent to:
String tmp = Test1 ();
return TMP;
In this way, it should be clear why the implementation of the results shown above!
All right, let's write this down! Hope that you read this article can have some harvest!
Summarize
didn't think of it! A small, seemingly simple finally statement block behind the hidden so many mystery. It seems that we should still carefully read Java-related basic documents, such as: Java language Specification, Java Virtual Machine specification, and so on, many difficult questions can be answered. Only the real understanding of the basic knowledge, to achieve the realm of ease of fluency!