In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-03-31 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/03 Report--
This article mainly explains "what are the pitfalls often encountered in using try-catch-finally". Friends who are interested may wish to have a look. The method introduced in this paper is simple, fast and practical. Let's let the editor take you to learn "what are the pitfalls often encountered in using try-catch-finally?"
Using return in pit 1:finally
If you use return in finally, even if there is a return operation in try-catch, the result will not be returned immediately, but will be returned after executing the statement in finally.
At this point, the problem arises: if there is a return statement in finally, the result in finally will be returned directly, thus mercilessly discarding the return value in try.
① counterexample code
Public static void main (String [] args) throws FileNotFoundException {System.out.println ("execution result:" + test ());} private static int test () {int num= 0; try {/ / num=1, no num++; return num is returned here } catch (Exception e) {/ / do something} finally {/ / num=2, return this value num++; return num;}}
The execution result of the above code is as follows:
Cause Analysis of ②
If there is a return statement in finally, then the return value in try-catch will be overwritten, and if the programmer does not find this problem when writing the code, it will lead to an error in the execution of the program.
③ solution
If there is a return return value in try-catch-finally, make sure that the return statement appears only once at the end of the method.
④ positive example code
Public static void main (String [] args) throws FileNotFoundException {System.out.println ("execution result:" + testAmend ());} private static int testAmend () {int num = 0; try {num = 1;} catch (Exception e) {/ / do something} finally {/ / do something} / ensure that the return statement appears return num only once here } code in 2:finally "does not execute"
If the above example is relatively simple, then the following example will give you a different feeling, just look at the code.
① counterexample code
Public static void main (String [] args) throws FileNotFoundException {System.out.println ("execution result:" + getValue ());} private static int getValue () {int num = 1; try {return num;} finally {num++;}}
The execution result of the above code is as follows:
Cause Analysis of ②
I thought the result of the implementation would be 2, but I never thought it would be 1. In the words of Master Ma, "I was careless and didn't flash."
One might ask: if you change the code to + + num, will the result be 2?
I'm sorry to tell you, no, the result of the execution is still 1. Then why is this happening? If we want to really understand it, we have to start with the bytecode of this code.
The bytecode generated by the above code is as follows:
/ / class version 52.0 (52) / / access flags 0x21 public class com/example/basic/FinallyExample {/ / compiled from: FinallyExample.java / / access flags 0x1 public () V L0 LINENUMBER 5 L0 ALOAD 0 INVOKESPECIAL java/lang/Object. () V RETURN L1 LOCALVARIABLE this Lcom/example/basic/FinallyExample; L0 L1 MAXSTACK = 1 MAXLOCALS = 1 / / access flags 0x9 public static main ([Ljava/lang/String;) V throws java/io/FileNotFoundException L0 LINENUMBER 13 L0 GETSTATIC java/lang/System.out: Ljava/io/PrintStream; NEW java/lang/StringBuilder DUP INVOKESPECIAL java/lang/StringBuilder. () V LDC "\ u6267\ u884c\ u7ed3\ u679c:" INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;) Ljava/lang/StringBuilder; INVOKESTATIC com/example/basic/FinallyExample.getValue () I INVOKEVIRTUAL java/lang/StringBuilder.append (I) Ljava/lang/StringBuilder; INVOKEVIRTUAL java/lang/StringBuilder.toString () Ljava/lang/String; INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String ) V L1 LINENUMBER 14 L1 RETURN L2 LOCALVARIABLE args [Ljava/lang/String L0 L20 MAXSTACK = 3 MAXLOCALS = 1 / / access flags 0xA private static getValue () I TRYCATCHBLOCK L0 L1 L2 null L3 LINENUMBER 18 L3 ICONST_1 ISTORE 0 L0 LINENUMBER 20 L0 ILOAD 0 ISTORE 1 L1 LINENUMBER 22 L1 IINC 0 L4 LINENUMBER 20 L4 ILOAD 1 IRETURN L2 LINENUMBER 22 L2 FRAME FULL [I] [java/lang/Throwable] ASTORE 2 IINC 0 1 L5 LINENUMBER 23 L5 ALOAD 2 ATHROW L6 LOCALVARIABLE num I L0 L6 0 MAXSTACK = 1 MAXLOCALS = 3}
A simplified version of these bytecodes is shown in the following figure:
To understand these bytecodes, you must first understand what these bytecodes represent, which can be found on the Oracle website (English document): https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-6.html
Brother Lei makes a simple translation of these bytecodes here:
Iconst is to push the value of type int into the Operand stack. Istore stores int to a local variable. Iload loads int values from local variables. Iinc increments the local variable by subscript. Ireturn returns a value of type int from the Operand stack. Astore stores references in local variables.
With this information, let's translate the bytecode above:
0 iconst_1 stores the value 1 1 istore_0 stores the data in the Operand stack at the location of the local variable 0 2 iload_0 from the local variable read value to the Operand stack 3 istore_1 stores 1 in the Operand stack 1 4 iinc 0 by 1 increments the elements of the local variable position 0 (+ 1) operation 7 iload_1 Load the value of 1 into the Operand stack 8 ireturn returns the int value in the Operand stack
From the above information, you may not be able to intuitively see the internal implementation process of this method, it doesn't matter that Brother Lei has prepared a method implementation flow chart for you:
From the above picture, we can see that before the execution of the finally statement (iinc 0,1), two pieces of information are stored in the local variable table, and both location 0 and location 1 store an int value of 1. Before executing finally (iinc 0,1), only the value of position 0 is accumulated, and then the value (1) of position 1 is returned to the Operand stack, so when the return operation (ireturn) is performed, the result with a return value of 1 is read from the Operand stack, so the final execution is 1 instead of 2.
③ solution
For the question of how the Java virtual machine compiles finally statement blocks, interested readers can refer to Section 7.13 Compiling finally in "The JavaTM Virtual Machine Specification, Second Edition". It details how the Java virtual machine compiles finally statement blocks.
In fact, the Java virtual machine inserts the finally statement block directly before the control transfer statement of the try statement block or the control transfer statement of the catch statement block as a subroutine (not knowing how to translate the subroutine, so as to avoid ambiguity and misunderstanding. However, there is another factor that cannot be ignored, that is, before executing the subroutine (that is, the finally statement block), the try or catch statement block retains its return value to the local variable table (Local Variable Table). After the subroutine execution is complete, the retained return value is restored to the Operand stack, and then it is returned to the caller of the method (invoker) through the return or throw statement.
So if there is a return operation in try-catch-finally, * * make sure that the return statement appears only once at the end of the method! * * this ensures that all operation codes in try-catch-finally will take effect.
④ positive example code
Private static int getValueByAmend () {int num = 1; try {/ / do something} catch (Exception e) {/ / do something} finally {num++;} return num;} code "not last" execution in 3:finally
① counterexample code
Public static void main (String [] args) throws FileNotFoundException {execErr ();} private static void execErr () {try {throw new RuntimeException ();} catch (RuntimeException e) {e.printStackTrace ();} finally {System.out.println ("execute finally.");}}
The execution result of the above code is as follows:
From the above results, we can see that the code in finally is not executed last, but before the catch prints the exception. Why?
Cause Analysis of ②
The real cause of the above problems is not try-catch-finally. When we open the source code of e.printStackTrace, we can see some clues. The source code is as follows:
As you can see from the figure above, when performing e.printStackTrace () and finally output information, you are not using the same object. Finally uses the standard output stream: System.out, while e.printStackTrace () uses the standard error output stream: System.err.println, which performs the same effect as:
Public static void main (String [] args) {System.out.println ("I am the standard output stream"); System.err.println ("I am the standard error output stream");}
The order of the execution results of the above code is also random, and the reason for this may be seen in the comments and documentation of the standard error output stream (System.err):
We simply make a simple translation of the above comments:
Standard error output stream. The stream is open and ready to accept output data. Typically, this stream corresponds to the host environment or user-specified display output or another output destination. By convention, even if the main output stream (out output stream) has been redirected to a file or other destination, the output stream (err output stream) can be used to display error messages or other information that should be brought to the user's immediate attention.
From the comments of the source code, we can see that the standard error output stream (System.err) and the standard output stream (System.out) use different stream objects, and even if the standard output stream is located to other files, it will not affect the standard error output stream. Then we can boldly guess that the two are executed independently, and in order to output stream information more efficiently, they are executed in parallel, so the result we see is that the print order is always random.
To verify this view, we redirect the standard output stream to a file, and then see if System.err can print properly. The implementation code is as follows:
Public static void main (String [] args) throws FileNotFoundException {/ / locates the information of the standard output stream to log.txt System.setOut (new PrintStream (new FileOutputStream ("log.txt")); System.out.println ("I am the standard output stream"); System.err.println ("I am the standard error output stream");}
The execution result of the above code is as follows:
When the program execution is complete, we find that a new log.txt file appears in the root directory of the project. Open this file and see the following results:
From the above results, we can see that the standard output stream and the standard error output stream execute independently of each other, and JVM makes them run in parallel for efficient execution, so the final result we see is that finally is executed before catch.
③ solution
Knowing the reason, the problem is easy to deal with, and we just need to change the output object in try-catch-finally to a unified output stream object to solve this problem.
④ positive example code
Private static void execErr () {try {throw new RuntimeException ();} catch (RuntimeException e) {System.out.println (e);} finally {System.out.println ("execute finally.");}}
After changing to a unified output stream object, I executed it manually n times and didn't find any problems.
Code in pit 4:finally "does not execute"
Does the code in finally have to be executed? I would not hesitate to say "yes" before, but after being beaten by society, I might answer like this: normally, the code in finally will be executed, but if there are special circumstances, the code in finally will not necessarily be executed, such as the following:
System.exit was executed in the try-catch statement
There is an endless loop in the try-catch statement
Power goes off or JVM crashes before the finally executes.
If either of these occurs, the code in finally will not be executed. Although it feels like a bit of a "rip off", Murphy's law tells us that if something is likely to happen, it will happen. So from a rigorous point of view, this view is still true, especially for beginners, it's easy to write an endless cycle that you can't find, isn't it?
① counterexample code
Public static void main (String [] args) {noFinally ();} private static void noFinally () {try {System.out.println ("I am try~"); System.exit (0);} catch (Exception e) {/ / do something} finally {System.out.println ("I am fially~");}}
The execution result of the above code is as follows:
From the above results, you can see that the code in finally is not executed.
② solution
Exclude the System.exit code from the code, except for business needs, but also note that if the System.exit code appears in the try-cacth, then the code in the finally will not be executed.
At this point, I believe you have a deeper understanding of "what are the pits often encountered in using try-catch-finally?" you might as well do it in practice. Here is the website, more related content can enter the relevant channels to inquire, follow us, continue to learn!
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.