一个问题:关于finally中return吞掉catch块中抛出的异常
今天遇到一个感觉很神奇的问题,记录一下问题以及自己分析问题的思路。
预警:不知道怎么看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块中抛出的异常的更多相关文章
- Java中主线程如何捕获子线程抛出的异常
首先明确线程代码的边界.其实很简单,Runnable接口的run方法所界定的边界就可以看作是线程代码的边界.Runnable接口中run方法原型如下: public void run(); 而所有的具 ...
- Java异常处理中finally中的return会覆盖catch语句中的return语句
Java异常处理中finally中的return会覆盖catch语句中的return语句和throw语句,所以Java不建议在finally中使用return语句 此外 finally中的throw语 ...
- springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。
springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑. 1.1 异常处理思路 系统中异常包括两类:预期异常和运行时异常RuntimeEx ...
- JavaWeb项目中获取对Oracle操作时抛出的异常错误码
最近在项目中碰到了这么一个需求,一个JavaWeb项目,数据库用的是Oracle.业务上有一个对一张表的操作功能,当时设置了两个字段联合的唯一约束.由于前断没有对重复字段的校验,需要在插入时如果碰到唯 ...
- 在SpringMVC中,当Json序列化,反序列化失败的时候,会抛出HttpMessageNotReadableException异常, 当Bean validation失败的时候,会抛出MethodArgumentNotValidException异常,因此,只需要在ExceptionHandler类中添加处理对应异常的方法即可。
在SpringMVC中,当Json序列化,反序列化失败的时候,会抛出HttpMessageNotReadableException异常, 当Bean validation失败的时候,会抛出Method ...
- lambda表达式中无法抛出受检异常!
抛出受检异常的时候,我们的接口应该带上throw关键字,但通过lambda表达式实现的Consumer的accept方法并不带有关键字,因此在lambda表达式中不能抛出受检异常必须把它吃掉
- 想抛就抛:Application_Error中统一处理ajax请求执行中抛出的异常
女朋友不是想抛就抛,但异常却可以,不信请往下看. 今天在MVC Controller中写代码时,纠结了一下: public async Task<ActionResult> Save(in ...
- .NET中如何在同步代码块中调用异步方法
更新记录 本文迁移自Panda666原博客,原发布时间:2021年7月2日. 在同步代码块中调用异步方法,方法有很多. 一.对于有返回值的Task 在同步代码块中直接访问 Task 的 Result ...
- try catch 块中debug时发现错误细节的一次记录
在解决已有代码的一个问题时,有一个try catch块,基本代码如下: try { //do something } catch { LogHelper.Debug(typeof(myHelper), ...
随机推荐
- 自己编译的openwrt在开机时一直打印jffs2_scan_eraseblock(): Magic bitmask 0x1985 not found...的错误提示
具体的输入信息是这样的. uboot> http Link down: eth0 Ethernet mode (duplex/speed): / Mbps HTTP server is star ...
- Java集合之Stack 源码分析
1.简介 栈是数据结构中一种很重要的数据结构类型,因为栈的后进先出功能是实际的开发中有很多的应用场景.Java API中提供了栈(Stacck)的实现,简单使用如下所示 package com.tes ...
- Wijmo 5 + Ionic Framework之:费用跟踪 App
Wijmo 5 + Ionic Framework之:费用跟踪 App 费用跟踪应用采用了Wijmo5和Ionic Framework创建,目的是构建一个hybird app. 我们基于<Mob ...
- 我的Android 4 学习系列之创建应用程序和Activity:Manifest、Application、Activity
目录 介绍Android应用程序组件,以及使用这些组件构建的各种Android应用程序 Android应用程序的生命周期 如何创建应用程序Manifest 如何使用外部资源提供对位置.语言和硬件配置的 ...
- Android项目---快递查询
快递查询,快递100上有更多接口信息 1.快递查询的接口是 快递公司的code值+快递单号 进行的网络查询.第一步,怎么将快递公司的名字转换成code值,传递给接口.下面是快递公司以及对应的code值 ...
- Fitnesse集成TestLink
TestLink作为开源测试管理工具,可以进行测试工程.测试计划以及执行计划的管理,而且TestLink团队提供了XML-PRC的接口供第三方工具调用,接口支持程度也比较好. Fitnesse作为开源 ...
- DropDownListFor的用法
Asp.Net MVC中DropDownListFor的用法 在Asp.Net MVC中可以用DropDownListFor的方式来让用户选择已定列表中的一个数值.用法不复杂,这里简单做一个记录. ...
- C#使用文件监控对象FileSystemWatcher 实现数据同步
在C#使用文件监控对象FileSystemWatcher 实现数据同步 2013-12-12 18:24 by 幕三少, 352 阅读, 3 评论, 收藏, 编辑 最近在项目中有这么个需求,就是得去实 ...
- C#接口总结
C#接口总结 浅析C#接口特点及实例应用 C#接口(interface)的掌握对我们开发有什么作用呢?C#接口的使用能够使我们的程序有什么改进?那么我们首先我们来看看C#接口特点以及具体的实例使用分析 ...
- 10.25最后的模拟赛DAY1 answer
QAQ太困了,大概说一下自己的思路: 其实这题很容易看错题目或是想错,就比如我个傻逼,一开始以为p+q一定等于n.... 咳咳...其实这题不用想太多,我们可以通过这n个字符串一个个假设正确或是不正确 ...