![56636e8cedc0355878d0e7cb9371dab2.png](https://i-blog.csdnimg.cn/blog_migrate/28f2f37deaabde778dfa806960249395.jpeg)
题图图片来源来自pixiv, 著作权归原作者所有,感谢作者, 如侵权必删.
往期文章目录
木兮木木:Java对象的内存布局
木兮木木:Java对象内存布局
木兮木木:Java虚拟机如执行方法调用的(二)?
木兮木木:Java虚拟机是如何执行方法调用的(一)?
木兮木木:Java垃圾回收(二)
木兮木木:Java垃圾回收(一)
木兮木木:Java虚拟机如何加载Java类?
木兮木木:Java代码是如何运行的?
木兮木木:Java常见垃圾收集器
木兮木木:Java垃圾回收算法
木兮木木:java创建对象过程
木兮木木:java虚拟机面试之内存区域划分
文章正文
Java里处理异常的两种方式是 捕捉异常和 抛出异常. 这两大要素共同实现程序控制流的非正常转移.
抛出异常分为 显示和 隐工两种方式. 「显示异常」的主体是应用程序, 它指的是在程序中使用「throw关键字」, 手动将异常实例「抛出」.
「隐式异常」的主体则是「Java虚拟机」. 它指的是Java虚拟机在执行过程中, 碰到无法继续执行的「异常状态」. 自动抛出「异常」. 比如说数组越界异常(ArrayIndexOutOfBoundsException).
捕捉异常涉及的三种代码块
- 「try」代码块
用来标记需要进行异常监控的代码.
2. 「catch」代码块
跟在「try」代码块之后, 用来捕获「try」代码块中触发的某种指定类型的异常.
除了声明所捕获的异常之外, 「catch」代码块还定义了针对该异常类型的「异常处理器」.
在Java中, 「try」代码块后面可以跟着多个「catch」代码块, 来捕获不同类型的异常.
Java虚拟机会从上至下匹配异常处理器. 因此, 「前面的catch」代码块所捕获的异常类型不能覆盖后边的, 否则编译器会报错.
3. 「finally」代码块
跟在「try」代码块和「catch」代码块之后. 用来声明一段「必定运行的代码」.
设计初衷是为了避免跳过某些关键的「清理代码」, 例如关闭已经打开的系统资源.
通常来说, 在程序正常执行的情况下, 「finally」里的代码块会在「try」代码块执行之后运行;
![01a0f0db2f9d3520555aa1b022ea6a06.png](https://i-blog.csdnimg.cn/blog_migrate/adcba93afa5ea306ca3af6ac3639ab6c.png)
如果「try」代码块里触发了异常, 如果该异常没有被捕获, 那么「finally」代码块会直接运行, 并且在运行之后重新抛出该异常.
![108cad4a79ab1d00f75882c7d86b377b.png](https://i-blog.csdnimg.cn/blog_migrate/57899fe75a8dde84534991bc4dfff945.png)
如果「catch」捕获到了异常, 「finally」代码块则在「catch」代码块之后运行;
![5611af1b73b614b01ef2dd372ba86cfe.png](https://i-blog.csdnimg.cn/blog_migrate/da8931108a0925e1752b889e9080a206.png)
如果catch代码中也触发了某种异常, 那么finally代码块同样会执行, 并会抛出在catch代码块触发的异常.
![07714fed84a65b92311a482f528c7fce.png](https://i-blog.csdnimg.cn/blog_migrate/601433e0a0bdea866a69714b34ef23ee.png)
如果finally代码块里也出现了异常, 那么只好中断当前finaly代码块的执行, 并往外抛出异常.
![afa3a73aaccf44e52c7baf018720a6cf.png](https://i-blog.csdnimg.cn/blog_migrate/f562dab1b73026491ac6bd52a276b057.jpeg)
异常的基本概念
Java中所有的异常都是「Throwable」类或者其子类实例.
![3e2462ea7036f9014f96e280e1b5009c.png](https://i-blog.csdnimg.cn/blog_migrate/17a27b7259f9678249b6daa49fc75919.png)
可以看出, Throwable有两大直接子类.
- Error
- 涵盖程序不应捕获的异常
- Exception
- 涵盖程序可能需要捕获并可能处理的异常.
Exception还有一个特殊的子类「RuntimeException」,用来表示「程序虽无法继续执行, 但是还可以抢救一下」的情况. 如索引值越界就是其中一种.
非检查异常
非检查异常(unchecked exception).
非检查异常包括两种
- Error
- RuntimeException
其它异常都属于「检查异常」(checked Exception)
在Java中所有的「检查异常(checked exception)」都需要程序「显示」地的捕获, 或者在方法声明中使用「throws」关键字标注.
通常情况下, 程序中的自定义异常应为「检查异常」, 以便最大利用Java编译器编译检查.
异常实例
异常实例的构造十分的昂贵.
这是由于在构造异常实例时, Java虚拟机便需要生成该异常的栈轨迹(stack trace). 该操作会会逐个访问当前线程的Java栈帧, 并且记录下各种调试信息, 包括栈帧所指向的方法名称、方法所在的类名称、方法所在的类名、文件名称及在代码中的第几行触发该异常.
在生成栈轨迹时, Java虚拟机会忽略掉异常构造器以及填充栈帧的Java方法(Throwable.fillInStackTrace), 直接从新建异常位置开始算起.
此外, Java虚拟机还会忽略标记为不可见的Java方法栈帧.
虚拟机捕获异常
在编译生成的字节码当中, 每个方法都附带一个「异常表」. 「异常表」的每一个条目代表一个异常处理.并且由「form」指针、「to」指针、「target」指针及所捕获的异常类构成.
这些指针的值是「字节码索引(bytecode index, bci)」用以定位字节码.
其中, from指针和to指针标示了该异常处理器所监控的范围. 如try代码块所覆盖的范围. target指针则指向异常处理器的起始位置, 例如catch代码块的起始位置.
总结
介绍了Java里异常处理机制.
Java里异常分类.
检查异常、非检查异常
下一篇看看JVM是如何处理异常的.
感谢阅读.
本篇完...