![259ac98b2c191e065f31270df69f2be7.gif](https://i-blog.csdnimg.cn/blog_migrate/705ac80ece43522d4984ec9efda43f95.gif)
一:概述
在日常code编码中,通常使用处理异常机制来保证程序运行的有效性,也经常听到老码农的异常要谨慎使用的声音,那么该怎么用才正确呢,会不会像有些人说有性能影响呢?更多信息请关注微信公众号: 白白家族
二:实战分析
对于种种疑问,最好的解决方式是刨根问底,一探究竟。
2.1 code案例分析
以下代码是最常见的异常应用,我们以此为起点,逐步探索
public static void main(String[] args) { int a = 1; try { a = 2; return; } catch (RuntimeException e1) { a = 3; } catch (Exception e2) { a = 4; } finanlly { a = 0; } }
2.2 案例源码分析
想知道上述案例代码做了哪些事情?那我们需要反编译上述源文件,从字节码执行过程来分析。(为形象展示给各大读者,这里屏蔽晦涩的编码指令,采用伪字节指令码来展示执行过程)
public static voidmain(java.lang.String[]);
Code:
0: 准备工作, 操作a = 1
1: try块开始
2: 操作 a = 2
3: finally块开始!
4: 操作a=0
5: 函数执行完毕,操作 return
6: RuntimeException 异常处理块开始!
7: 操作 a = 3
8: finally处理块
9: 操作a = 0
10: RuntimeException 异常处理块结束, 操作 return
11: Exception e2异常处理块开始!
12: 操作 a = 4
13: finally处理块
14: 操作 a = 0;
15: Exception e2 异常处理块结束, 操作 return
Exception table: // 异常表
from to target type
1 2 6 RuntimeException
1 2 11 Exception
以上流程也不难懂,我们先从了解上述关键字说起:
1) code: 代表指令顺序,表示程序运行的前后顺序,按照序号从小到大以此执行,例如:1执行完就会执行2
2) exception table:异常表,这张表中的每个行记录,记录着代码中定义的异常信息。
其中每一行记录有4个字段信息:
from : 异常的开始地址
to: 异常的结束地址,
target: 异常的处理起始位
type: 异常类名称
拿第一行 1 2 6 RuntimeException 举例说明,代码执行在1-2中,如遇到RuntimeException 异常,则从指令6开始运行
2.3 追溯上述指令码执行过程
1> 如果异常没有发生,指令码依次运行code=1,2,3,4,5;其中不会运行 6到10 和11到15(如上示例), 也就不会查exception table表,所以如果没有发生异常,写不写try catch对性能是没有消耗的。
2> 当代码抛出了异常时,首先拿着抛出位置到exception table表中查找对应的开始/结束地址,然后匹配出target位置,进行异常处理(如示例注释)。这一步操作在内存查找处理中,是不会影响性能的。
三,经验总结:
但是在实际编码时,我们常常被要求尽量减少try-catch语句块,这是为什么呢?
原因是创建异常对象的代价是非常大的。当程序抛出异常后,它会创建一个异常对象(如案例中的e1、e2对象),收集堆栈异常信息(也就是e.printStackTrace()中展示的信息),并从当前环境中弹出对异常对象的引用,将这些信息反馈给我们。
综上所述,在使用try-catch异常机制时,一方面需要考虑业务层面的必要性,另一方面考虑代码层面异常是否有必要被创建以及被抛出的频率上,根据实际需求,适当地使用try-catch。
更多信息请关注微信公众号: 白白家族