前言:有java编程基础的人对java的异常处理机制都会有一定了解,而且可能感觉使用起来也比较简单,但如果在try catch finally语句块中遇到return语句,开发者可能就会遇到一些逻辑问题,甚至步入编程的陷阱。不信,我们先看看一段小程序,读者可以分析其逻辑然后猜测其输出结果:

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);

}

}

}

请先停止继续阅读,试着说出其运行结果。

如果你不能很自信地正确说出这段程序的执行逻辑和结果,那本文就值得你一读,如果你可以,请忽略本文。

执行结果是:

– catch –

b:true

– finally –

b:false

false

你说对了吗?

正文

首先我们了解java的异常处理机制中try、catch、finally的基本执行逻辑。

  • try:包裹可能引发异常的代码
  • catch:可以有多个catch块,一个代码块对应一种异常类型,表明该catch块用于处理此类型的异常。
  • finally:主要用于回收在try块里使用的物理资源(比如数据库连接、网络连接和磁盘文件等),这些物理资源必须显示回收,因为java的垃圾回收机制不会回收任何的物理资源,垃圾回收机制只回收堆内存中对象所占用的内存。异常机制保证finally块内的代码总是被执行,除非在try块或者catch块中调用了退出虚拟机的方法(即System.exit(1);),此时程序直接退出,不再执行finally块。

首先明确其语法结构:

1. try块必存在, catch块和finally块都是可选的,但两者需至少出现其中之一,也可以同时出现;

2. 可以有多个catch块,捕获父类异常的catch块必须位于捕获子类异常的后面,即设计多个catch块捕获异常时要先捕获小的异常,再捕获大的异常。一般,try块后只有一个catch块会被执行,绝不可能有多个catch块被执行,除非使用continue开始下次循环时,再次执行到这个try、catch块,而捕获处理其他catch的异常;

3. 多个catch块必须位于try块之后,finally块必须位于所有catch块之后。

下面我们分情况讨论try、catch、finally中含有return的情况。

示例一(try、catch、finally中都有return):

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;

}

}

}

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 --");

}

}

}

执行结果:

– 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);

}

}

}

代码中我们将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++;

}

}

}

答案是:

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;

}

}

}

运行结果:

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

Java中try catch finally语句中含return语句的执行情况总结-编程陷阱的更多相关文章

  1. java finally中含return语句

    <java核心技术卷一>中提到过:当finally子句包含return 语句时(当然在设计原则上是不允许在finally块中抛出异常或者 执行return语句的,我不明白为何java的设计 ...

  2. Java中try、finally语句中有return时的执行情况 [转]

    原文:http://kingj.iteye.com/blog/1436761 在Java中当try.finally语句中包含return语句时,执行情况到底是怎样的,finally中的代码是否执行,大 ...

  3. try,catch,finally含return时的执行顺序及丢失的伪例

    最近面试遇到一个之前也看到过但没去看一下的问题.就是有return情况下的try,catch,finally的执行顺序. 今天写了下. 先看顺序问题.总结如下: 一:finally中没有写return ...

  4. 0016 Java学习笔记-异常-如果try-catch-finally中都存在return语句会怎样?

    上午在搜索"System.runFinalization"的时候,搜到 http://www.cnblogs.com/Skyar/p/5962253.html ,其中有关于try- ...

  5. try、catch、finally都有return语句时执行哪个

    任何执行try 或者catch中的return语句之前,都会先执行finally语句,如果finally存在的话.如果finally中有return语句,那么程序就return了,所以finally中 ...

  6. 面试题:try,catch,finally都有return语句时执行哪个 已看1

    1.不管有木有出现异常,finally块中代码都会执行: return 先执行 把值临时存储起来, 执行完finally之后再取出来 值是不会改变的2.当try和catch中有return时,fina ...

  7. finall语句是在return语句执行前被执行还是执行后被执行?

    finally对try...catch的影响和执行时间 一.finally语句不被执行的两种情况 我们在学习抛出异常的时候学习到了finally语句块,这个语句块一般会被执行,但是有两种情况下不被执行 ...

  8. Java问题解读系列之基础相关---含继承时的执行顺序

    今天来研究一下含继承.静态成员.非静态成员时Java程序的执行顺序: 一.不含继承,含有静态变量.静态代码块 创建一个子类,该类包含静态变量.静态代码块.静态方法.构造方法 /** * @create ...

  9. java中 try catch finally和return联合使用时,代码执行顺序的小细节

    代码1测试 public static void main(String[] args) { aa(); } static int aa() { try { int a=4/0; } catch (E ...

随机推荐

  1. BZOJ1491:1491: [NOI2007]社交网络

    1491: [NOI2007]社交网络 Time Limit: 10 Sec  Memory Limit: 64 MBSubmit: 2204  Solved: 1175[Submit][Status ...

  2. H5C3--盒子模型

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. vim Tab的设置问题

    VIM 中处理 TAB 异常的方便. 键盘上的 TAB 键,与文件中的 TAB 符号一定要区分开,这是两个概念. 显示当前文件中的 Tab /\t 实际上,就是查询 \t 符号. shiftwidth ...

  4. warning: deprecated conversion from string constant to 'char*

    warning: deprecated conversion from string constant to 'char* #include<iostream> using namespa ...

  5. 读书笔记--Spring in Action 目录

    1.Spring之旅 1.1 简化java 开发 1.1.1 激发pojo 的潜能 1.1.2 依赖注入1.1.3 应用切面1.1.4 使用模板消除样板式代码1.2 容纳你的bean1.2.1 与应用 ...

  6. mybatis深入理解(七)-----MyBatis缓存机制的设计与实现

    缓存设计 MyBatis将数据缓存设计成两级结构,分为一级缓存.二级缓存: 一级缓存是Session会话级别的缓存,位于表示一次数据库会话的SqlSession对象之中,又被称之为本地缓存.一级缓存是 ...

  7. 木卯先生的笔记---Date类、DateFormat类和Calendar类

    1.Date类 1.1 简介 Date类是 java.util 包下面的类,表示特定的瞬间,精确到毫秒. 1.2 方法 1.2.1 Date() 构造方法 public Date() :分配 Date ...

  8. 2019.8.10 NOIP模拟测试16 反思总结【基本更新完毕忽视咕咕咕】

    一如既往先放代码,我还没开始改… 改完T1滚过来了,先把T1T2的题解写了[颓博客啊] 今天下午就要走了,没想到还有送行的饯别礼,真是欣喜万分[并没有] 早上刚码完前面的总结,带着不怎么有希望的心情开 ...

  9. 【Codeforces Round #430 (Div. 2) B】Gleb And Pizza

    [链接]点击打开链接 [题意] 在这里写题意 [题解] 根据圆心到原点的距离这个东西判断一下圆在不在那个环里面就好 [错的次数] 0 [反思] 在这了写反思 [代码] #include <cst ...

  10. LUOGU 3089 后缀排序(模板)

    传送门 解题思路 这是一个神奇的算法,sa[i]表示排名第i为的元素是啥,rk[i]表示第i个元素排名是啥.然后使用基数排序+倍增的思想去处理.主要是参考的这位大佬的博客(https://www.cn ...