Summary of the execution of the return statement in the try catch finally statement in Java - programming trap

Foreword : People with java programming foundation will have a certain understanding of java's exception handling mechanism, and may feel that it is relatively simple to use, but if you encounter a return statement in a try catch finally statement block, developers may encounter some Logic problems, and even into the trap of programming. If you don't believe me, let's take a look at a small program, the reader can analyze its logic and guess its output:

public class Test {
    public static void main(String[] args) {
        Test t = new Test();
        System.out.println(t.Test1());
    }

    public boolean Test1() {
        boolean b = true;
        try {
            int i = 10 / 0; 
            System.out.println("i = " + i);
            return true; 
        } catch (Exception e) {
            System.out.println(" -- catch --");
            System.out.println("b:" + b);
            return b = false; 
        } finally {
            System.out.println(" -- finally --");
            System.out.println("b:" + b);
        }
    }
}

Please stop reading and try to tell the results of its operation. 
If you can't confidently say correctly the execution logic and results of this program, then this article is worth reading, if you can, please ignore this article. 
The execution result is: 
– catch – 
b:true 
– finally – 
b:false 
false 
Are you right?

Text 
First, we understand the basic execution logic of try, catch, and finally in the exception handling mechanism of java.

  • try: wraps code that may throw exceptions
  • catch: There can be multiple catch blocks, one code block corresponds to one type of exception, indicating that the catch block is used to handle exceptions of this type.
  • finally: mainly used to recycle the physical resources used in the try block (such as database connections, network connections and disk files, etc.), these physical resources must be displayed for recycling, because the garbage collection mechanism of java will not recycle any physical resources, garbage collection The mechanism only reclaims the memory occupied by objects in heap memory . The exception mechanism ensures that the code in the finally block is always executed unless the method to exit the virtual machine (ie System.exit(1);) is called in the try block or catch block , at which time the program exits directly without executing the finally block. .

First, clarify its grammatical structure: 
1. The try block must exist, the catch block and the finally block are optional, but at least one of the two must appear, or they can appear at the same time; 
2. There can be multiple catch blocks to capture the parent The catch block of the class exception must be located after the catch subclass exception, that is, when designing multiple catch blocks to catch exceptions, first catch small exceptions, and then catch large exceptions . Generally, only one catch block will be executed after the try block, and there can never be more than one catch block executed, unless you use continue to start the next loop, execute the try and catch blocks again, and catch exceptions from other catches; 
3. Multiple catch blocks must be placed after try blocks, and finally blocks must be placed after all catch blocks.

Next, we will discuss the cases where try, catch, and finally contain return. 
Example 1 (return in try, catch, finally):

public class Test {
    public static void main(String[] args) {
        Test t = new Test();
        System.out.println(t.Test1());
    }

    public boolean Test1() {
        try {
            int i = 10 / 0; // 抛出 Exception,try中其他代码不执行,进入catch
            System.out.println("i = " + i);
            return true; 
        } catch (Exception e) {
            System.out.println(" -- catch --");
            return false;
        } finally {
            System.out.println(" -- finally --");
            return true;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

Eclipse中,这段代码会出警告:finally block does not complete normally。译为:finally块不能正常完成。Java不建议在finally块中使用renturn或throw等导致方法终止的语句,否则将会导致try块、catch块中的return、throw语句失效。但是仍然可以执行,结果为: 
– catch – 
– finally – 
true 
这里最后打印的true即时finally中返回的true。至于为什么会覆盖,这个涉及到JVM底层字节码的具体实现和一些指令操作,如果没有JVM和计算机组成原理以及操作系统的相关基础知识是较难以理解的,有余力的开发者可以深入研究。

实例二(try、catch中有return,finally中无):

public class Test {
    public static void main(String[] args) {
        Test t = new Test();
        System.out.println(t.Test1());
    }

    public boolean Test1() {
        try {
            int i = 10 / 0; // 抛出 Exception,try中其他代码不执行,进入catch
            System.out.println("i = " + i);
            return true; // 不会执行
        } catch (Exception e) {
            System.out.println(" -- catch --");
            return false; // Exception 抛出,获得了返回false的机会
        } finally {
            System.out.println(" -- finally --");
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

执行结果: 
– catch – 
– finally – 
false

断点调试可发现其逻辑:在finally执行后,又回到catch语句里面的return上,然后返回这句中的false。然后,很多人就会认为,甚至很多技术博客都写到:只有finally块执行完成之后,才会回来执行try或者catch块中的return语句。 但是!!真的是这样吗?也许是说这话的人知道意思但表达不够清楚,以至于让笔者觉得是误人子弟,try、catch中的rerun语句的逻辑是否执行?执行完finally块后一定会返回去执行try、catch吗?我们回过头来看开篇的那段代码:

public class Test {
    public static void main(String[] args) {
        Test t = new Test();
        System.out.println(t.Test1());
    }
    public boolean Test1() {
        boolean b = true;
        try {
            int i = 10 / 0; 
            System.out.println("i = " + i);
            return true; 
        } catch (Exception e) {
            System.out.println(" -- catch --");
            System.out.println("b:" + b);
            return b = false; 
        } finally {
            System.out.println(" -- finally --");
            System.out.println("b:" + b);
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

代码中我们将catch中的return语句加上了一个赋值操作,断点调试,可以发现,程序执行到int i = 10 / 0; 语句后跳出catch块,并完整执行到return b = false; 然后进入finally块,执行两条输出语句,第二条System.out.println(“b:” + b);这里的b变成了false!,说明之前return中的赋值语句有执行,执行完finally后,程序再次进入catch中的return,返回给调用者。所以事实是,当Java程序执行try块、catch块遇到return语句时,当系统执行完return语句之后,并不会立即结束该方法,而是去寻找该异常处理流程中是否包含finally块,如果没有finally块,方法终止,返回相应的返回值。如果有finally块,系统立即开始执行finally块——只有当finally块执行完成后,系统才会再次跳回来根据return语句结束方法。如果finally块里使用了return语句来导致方法结束,则finally块已经结束了方法,系统不会跳回去执行try、catch块里的任何代码。

依照此标准,下列程序应该输出什么?

public class Test {
    public static void main(String[] args) {
        Test t = new Test();
        System.out.println(t.test());
    }

    public static int test() {
        int count = 5;
        try {
            return ++count;
        } catch (Exception e) {
            // TODO: handle exception
        } finally {
            System.out.println("finally()执行");
            return count++;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

答案是: 
finally()执行 
6

测验:

public class Test {
    public static void main(String[] args) {
        int a  = test();
        System.out.println(a);
    }

    public static int test() {
        int count = 5;
        try {
            throw new RuntimeException("测试异常1");
        }catch (RuntimeException e) {
            System.out.println(e.toString());
            throw new RuntimeException("测试异常2");
        }catch (Exception e) {
            System.out.println(e.toString());
            return 2;
        } finally {
            System.out.println("finally()执行");
            return count;
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

运行结果: 
java.lang.RuntimeException: 测试异常1 
finally()执行 
5

如果注释掉语句:return count; 
则运行结果是先打印出 
“java.lang.RuntimeException: 测试异常1 
finally()执行” 
后再抛出测试异常2,程序异常结束。

简而言之程序在catch中执行throw语句时并不会立即抛出异常,而是去寻找该异常处理流中是否包含finally块。如果没有finally块,程序立即抛出异常;如果有finally块,程序立即开始执行finally块——只有当finally块执行完成后,系统才会再次跳回来抛出异常。如果finally块里使用return语句来结束方法,系统将不会跳回catch块去抛出异常。

如果去掉catch块和finally中的return语句,则运行结果为: 
先打印“System.out.println(“finally()执行”);”再抛出异常1,方法异常结束。 
如果去掉catch块,保留finally中的return语句,则运行结果为: 
“finally()执行 
5” 
方法正常结束,不会再抛出异常。

实践是检验真理的唯一标准。学会用质疑的眼光求真,用踏实的态度去实践,是每一个成熟的技术人员所必须的。

请尊重原创,转载请注明出处: 

http://blog.csdn.net/daijin888888/article/details/48369809

转载于:http://blog.csdn.net/daijin888888/article/details/48369809

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325773551&siteId=291194637