今天遇到一个感觉很神奇的问题,记录一下问题以及自己分析问题的思路。

预警:不知道怎么看java字节码的朋友可能需要先看一下如何阅读java字节码才能看懂后面的解释。

我有一段程序:

public class Test {
public static void main(String[] args) {
try {
int a = 1 / 0;
} catch (Exception e) {
throw e;
}
}
}

这个程序的运行结果相信大家都能猜到:

在main方法里捕获异常没有处理直接往上层抛,最后异常打印到了控制台。

现在我给这段代码加一个finally块,里面写个return,大家猜测一下运行结果:

public class Test {
public static void main(String[] args) {
try {
int a = 1 / 0;
} catch (Exception e) {
throw e;
} finally {
return;
} }
}

如果你觉得运行结果会是程序正常运行没有报任何错误,那么你可以不用再继续读了,因为运行结果确实如此。

如果你像笔者一样觉得运行结果应该和之前一样,程序依然会抛出异常,那么请继续往下看。

笔者之前的想法是,这段程序运行的时候,会先执行try中的语句,然后发生了异常进入catch块,在catch块中抛出异常,此时程序已经结束,finally中的return其实没有什么意义。

但是根据运行结果来看显然不是这样的,于是笔者将前后两段程序都用javap -verbose命令反汇编,结果如下(去掉了常量池等无关信息):

不带finally的程序反汇编结果:

public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: iconst_1
1: iconst_0
2: idiv
3: istore_1
4: goto 10
7: astore_1
8: aload_1
9: athrow
10: return

Exception table:
from to target type
0 4 7 Class java/lang/Exception

我们来分析一下这段代码的含义:

第0~4行是程序执行的正常逻辑:

0: iconst_1  //将int 1推至栈顶
1: iconst_0  //将int 0推至栈顶
2: idiv  //将栈顶两int型数值相除(即1/0)并将结果压入栈顶
3: istore_1  //将栈顶的int值存入第二个本地变量(即赋值给a)
4: goto 10  //跳转到第10行return

然后异常表定义了执行0~4行时,如果发生异常就跳转到第7行,实际执行时会在第2行发生异常,因此我们看一下发生异常会走的逻辑:

0: iconst_1  //将int 1推至栈顶
1: iconst_0  //将int 0推至栈顶
2: idiv  //预计将栈顶两int型数值相除(即1/0)并将结果压入栈顶,此时发生异常,实际将异常的引用放到栈顶,根据异常表跳到第7行

7: astore_1  //将栈顶的异常引用存入第二个本地变量
8: aload_1  //将异常放置到栈顶
9: athrow  //抛出栈顶异常
10: return

带finally块的反汇编结果:


public static void main(java.lang.String[]);
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=2, args_size=1
0: iconst_1
1: iconst_0
2: idiv
3: istore_1
4: goto 11
7: astore_1
8: aload_1
9: athrow
10: pop
11: return

Exception table:
from to target type
0 4 7 Class java/lang/Exception
0 10 10 any

 

仔细观察,有finally块的代码的汇编代码和无finally块的汇编代码几乎完全一致,只是多了一个pop,即我标黄的部分。

从pop的位置来看,是已经执行了athrow抛出了栈顶的异常之后执行的,那么这个pop弹出的是什么东西呢?为什么多了这个pop异常就不会抛出了呢?

我还要再查一下资料(逃~)

附上athrow命令的解释:

Description

Removes objectref (a reference to an object) from the operand stack, and 'throws' the exception represented by that object. objectref is an instance of Throwable or one of its subclasses.

To throw an exception, the system searches for a handler for objectref's class in the exception table of the currently active method.

If no handler is found, the current method's frame is discarded, its invoker's frame is reinstated, and the exception is immediately rethrown. This process is repeated until a handler is found or until there are no more procedures on the callstack (at which point, the current thread dies, typically printing out an error message).

If a handler is found, the operand stack of the active method is cleared, objectref is pushed on the operand stack of the current method, and execution continues at the first instruction of the handler.

See Chapter 10 for a full description of exceptions in the JVM.

一个问题:关于finally中return吞掉catch块中抛出的异常的更多相关文章

  1. Java中主线程如何捕获子线程抛出的异常

    首先明确线程代码的边界.其实很简单,Runnable接口的run方法所界定的边界就可以看作是线程代码的边界.Runnable接口中run方法原型如下: public void run(); 而所有的具 ...

  2. Java异常处理中finally中的return会覆盖catch语句中的return语句

    Java异常处理中finally中的return会覆盖catch语句中的return语句和throw语句,所以Java不建议在finally中使用return语句 此外 finally中的throw语 ...

  3. springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。

    springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑. 1.1 异常处理思路 系统中异常包括两类:预期异常和运行时异常RuntimeEx ...

  4. JavaWeb项目中获取对Oracle操作时抛出的异常错误码

    最近在项目中碰到了这么一个需求,一个JavaWeb项目,数据库用的是Oracle.业务上有一个对一张表的操作功能,当时设置了两个字段联合的唯一约束.由于前断没有对重复字段的校验,需要在插入时如果碰到唯 ...

  5. 在SpringMVC中,当Json序列化,反序列化失败的时候,会抛出HttpMessageNotReadableException异常, 当Bean validation失败的时候,会抛出MethodArgumentNotValidException异常,因此,只需要在ExceptionHandler类中添加处理对应异常的方法即可。

    在SpringMVC中,当Json序列化,反序列化失败的时候,会抛出HttpMessageNotReadableException异常, 当Bean validation失败的时候,会抛出Method ...

  6. lambda表达式中无法抛出受检异常!

    抛出受检异常的时候,我们的接口应该带上throw关键字,但通过lambda表达式实现的Consumer的accept方法并不带有关键字,因此在lambda表达式中不能抛出受检异常必须把它吃掉

  7. 想抛就抛:Application_Error中统一处理ajax请求执行中抛出的异常

    女朋友不是想抛就抛,但异常却可以,不信请往下看. 今天在MVC Controller中写代码时,纠结了一下: public async Task<ActionResult> Save(in ...

  8. .NET中如何在同步代码块中调用异步方法

    更新记录 本文迁移自Panda666原博客,原发布时间:2021年7月2日. 在同步代码块中调用异步方法,方法有很多. 一.对于有返回值的Task 在同步代码块中直接访问 Task 的 Result ...

  9. try catch 块中debug时发现错误细节的一次记录

    在解决已有代码的一个问题时,有一个try catch块,基本代码如下: try { //do something } catch { LogHelper.Debug(typeof(myHelper), ...

随机推荐

  1. SAE设置记录:修改config.yaml实现地址重写和修改固定链接

    刚搭建完sae博客后闲置下来了,偶尔写两篇文章,最近想整理整理sae,于是开始. 刚新建完博客修改固定链接,可是保存后直接访问出现问题,访问不到文章了,而且我的博客地址前面会出现"1.&qu ...

  2. 《Programming Hive》读书笔记(两)Hive基础知识

    <Programming Hive>读书笔记(两)Hive基础知识 :第一遍读是浏览.建立知识索引,由于有些知识不一定能用到,知道就好.感兴趣的部分能够多研究. 以后用的时候再具体看.并结 ...

  3. QtNetwork说明(两)使用QT实现360的ctrl+ctrl特征

    头文字说明: <span style="font-size:18px;">#ifndef GOOGLESUGGEST_H #define GOOGLESUGGEST_H ...

  4. WPF随手小记之二 ——改变DataGrid样式

    大部分时候我们并不需要手动改变DataGrid的样式,因为用默认的其实也看得过去,而且在DataGrid中的数据也远比外观重要. 但总有时候,我们需要做点必要的UI修改来使DataGrid更美观. 一 ...

  5. nginx配置文件中的location详解

    location 语法:location [=|~|~*|^~] /uri/ { … } 默认:否 上下文:server 这个指令随URL不同而接受不同的结构.你可以配置使用常规字符串和正则表达式.如 ...

  6. Repository 仓储,你的归宿究竟在哪?(上)

    Repository 仓储,你的归宿究竟在哪?(上) 写在前面 写这篇博文的灵感来自<如何开始DDD(完)>,很感谢young.han兄这几天的坚持,陆陆续续写了几篇有关于领域驱动设计的博 ...

  7. Spring.Net+Nhibernate+Asp.Net Mvc 框架

    搭建你的Spring.Net+Nhibernate+Asp.Net Mvc 框架 (一)搭建你的环境 使用这套框架不是很长时间.但也基本应用了几个项目中了.在此和大家分享一下我是怎样一步一步搭建此框架 ...

  8. ODBC操作数据库

    /*ODBC使用步骤:(ODBC数据源由微软平台提供) * 1.配置ODBC数据源(控制面板->管理工具->ODBC数据源) * 2.加载并注册驱动程序,导入java.sql.*包 * 3 ...

  9. mvc4中jquery-ui日期控件datepicker的应用

    mvc4中jquery-ui日期控件datepicker的应用 本文适合mvc中日期选择需要的同学: 假设读者已经具备了mvc4和javascript中的相关知识 一. 开始项目之前把项目中目录:/C ...

  10. vj1011:记忆化搜索

    vj1011:记忆化搜索 这题就是很简单的记忆化搜索即可,和经典题目滑雪简直一模一样 对于记忆化搜索,我也是暑假看了ccy大神的题解才有所领悟的 其实也就是DFS+mark 主要的部分 int sea ...