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

预警:不知道怎么看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. Building Modern Web Apps-构建现代的 Web 应用程序

    Building Modern Web Apps-构建现代的 Web 应用程序 视频长度:1 小时左右 视频作者:Scott Hunter 和 Scott Hanselman 视频背景:Visual ...

  2. knockout笔记

    根据汤姆大叔博客总结-笔记: =============<script type="text/javascript"> $(function () { var View ...

  3. 【分享】LateX排版软件学习教程合集

    来源于:http://www.hejizhan.com/html/xueke/416/x416_13.html  LATEX2e科技排版指南.pdf 8.3 MB  An Example LaTeX ...

  4. ASP.NET Web API的HttpController是如何被激活的?

    ASP.NET Web API的HttpController是如何被激活的? HttpController与HttpControllerDescriptor 程序集的解析 HttpController ...

  5. linux 编程技术

    linux 编程技术No.1前期准备工作 GCC的编译过程分为预处理.生成汇编代码.生成目标代码和链接成可执行文件等4个步骤. 使用vim编写C 文件 : [lining@localhost prog ...

  6. CLR 的执行模型(2)

    第一章 CLR 的执行模型(2) 本篇内容大纲 Framework 类库(Framework Class Library , FCL) 通用类型系统(Common Type System,CTS) 公 ...

  7. 利用MARQUEE实现正在处理效果

    ASP.NET服务器端事件利用MARQUEE实现正在处理效果   前言:ASP.NET同仁们应该都遇到过当触发一个比较耗时的服务器端事件时,页面会处在一个等待的状态(即假死状态),用户体验非常不好,很 ...

  8. [原]关于在 iOS 中支持 DLNA

    目前国内的视频播放器中,支持 DLNA 播放比较好的是:腾讯视频. 刚才反编了一下他们的代码,发现使用了一个开源库, 地址:https://github.com/FuruyamaTakeshi/DLN ...

  9. c语言,string库函数itoa实现:将int转换为char*

    原型:char  *itoa(int   value,char   *string)        功能:将整数value转换成字符串存入string,默认为十进制;      说明:返回指向转换后的 ...

  10. jQuery获取动态生成的元素

    需求描述:页面上可以动态添加数据,比如table,点击按钮可以动态添加行.又或页面 加载时table数据是通过ajax从后台获取的.而这时我们想要获取其中的某个值,又该如何获取呢? 如果是要通过某个事 ...