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

预警:不知道怎么看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. VS2015 ASP.NET5 Web项目

    VS2015 ASP.NET5 Web项目结构浅析   前言 本文个人同步博客地址http://aehyok.com/Blog/Detail/76.html 在安装好VS2015之后,一直想看看新版本 ...

  2. 我的Android 4 学习系列之创建应用程序和Activity:Manifest、Application、Activity

    目录 介绍Android应用程序组件,以及使用这些组件构建的各种Android应用程序 Android应用程序的生命周期 如何创建应用程序Manifest 如何使用外部资源提供对位置.语言和硬件配置的 ...

  3. 利用sqlclr实现数据库服务器端数据加密解密

    在公司中一同事用sqlclr写数据迁移自动化执行脚本,发现他在执行脚本时对数据进行了加密. 个人觉得利用sqlclr对数据进行加密是一个解决数据网络安全传输的不错的方案. 以下是一个小的案例: --- ...

  4. sqlclr返回数据集案例

    ----------------------------------------------返回一张表,但只有一条数据,最后一次设置的. [Microsoft.SqlServer.Server.Sql ...

  5. 创建自己的github代码库

    前提: 1.安装git 2.在github上创建一个repository(库),如名字叫Hello-World,则git地址为https://github.com/username/Hello-Wor ...

  6. 调式WP程序报0x80131500错误的解决办法

    在虚拟机上安装了win8系统和VS2013,但是在允许第一个WP程序时,居然报0x80131500错误信息,经查询原来是VS2013需更新的问题,如果你用的是VS2012,但是又系统升级到了win8. ...

  7. ASP.NET开发的大型网站有哪些架构方式

    谈谈用ASP.NET开发的大型网站有哪些架构方式(成本) 在上篇文章里(http://www.cnblogs.com/ms0017/archive/2011/07/26/2117676.html),列 ...

  8. 生成自己的Webapi帮助文档(二)

    经过今天一上午的修改,已经有个基础的框架了,其它功能只能是在实际使用中发现一个修改一个了. 以下是生成的结果示例: 相比昨天,几个Model都有修改,这里就不一一贴代码了,放个代码包上来,有需要的自己 ...

  9. iOS基础 - 内存分析

    一.内存的重要性 程序是否可用的最根本的制约因素. 十分影响用户体验. 程序的crash有很多情况都是内存的原因. 二.iOS平台内存警告机制 iOS系统不支持虚拟内存,一旦出现内存不足的情况,iOS ...

  10. Kraken.js!

    Hello Kraken.js! 前言 kraken.js 由paypal 公司开源的一个用于快速开发基于Express.js框架应用的快速开发工具, 因为kraken 并没有在Express.js基 ...