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

预警:不知道怎么看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. printf详解

    用了这么多年c了,今天想编个小程序练练手,忽然发现对于printf这个函数并不甚了解.上网查了查,下面是对printf()的详解: 函数原型: #include <stdio.h> int ...

  2. PHP:Cannot modify header information - headers already sent by错误的解决方案

    <?php ob_start();setcookie("username","test",time()+3600);echo "the user ...

  3. 【原版的:参赛作品】窥秘懒---android打开下拉手势刷新时代

    小飒的成长史原创作品:窥视懒人的秘密---android下拉刷新开启手势的新纪元转载请注明出处 **************************************************** ...

  4. 我的Android 4 学习系列之数据库和Content Provider

    目录 创建数据库和使用SQLite 使用Content Provider.Cusor和Content Value来存储.共享和使用应用程序数据 使用Cursor Loader异步查询Content P ...

  5. [转] 关于 Eclipse 导出 Android项目 JavaDoc 详细过程

    关于Eclipse 导出JavaDoc过程中,遇到的问题 ,google 了一下 ,网友们 总说不一 ,最终 还是 搞定了 现在分享给大家 希望对大家有所帮助 用Eclipse默认的 JavaDoc需 ...

  6. 学习selenium python版最初的一个小想法

    这个还是我在刚开始学习selenium的时候做的,自己觉得有点意思,在接下来我会基于目前我对于selenium的一些深入研究,写下我对selenium的理解以及UIAutomation的一些理解,以此 ...

  7. 动动手,写个knockout的分页模板

    最近一个项目用ASP.NET + knockout开发,很多列表页面都带分页,于是就有了写一个公共的分页模板的想法. 先把template写好: <script type="text/ ...

  8. 利用XCode来进行IOS的程序开发

    利用XCode来进行IOS的程序开发 本随笔系列主要介绍从一个Windows平台从事C#开发到Mac平台苹果开发的一系列感想和体验历程,本系列文章是在起步阶段逐步积累的,希望带给大家更好,更真实的转换 ...

  9. MFC双缓冲和裁剪问题导致闪烁

    问题描述: 应用场景:在对话框中,自定义一个MFC图形控件(为了描述方便,暂定为HSPaintControl),控件覆盖整个对话框的客户区,属于最底层的控件,在这之上放置了很多其他的小图形控件. 问题 ...

  10. iOS基础 - 控制器管理

    一.Container 一个iOS的app很少只由一个ViewController组成,除非这个app极其简单.当app中有多个ViewController的时候,我们就需要对这些ViewContro ...