java异常详解
一、 异常图解
二、异常分类
- Throwable
Throwable是 Java 语言中所有错误或异常的超类。
Throwable包含两个子类: Error 和 Exception。它们通常用于指示发生了异常情况。
Throwable包含了其线程创建时线程执行堆栈的快照,它提供了printStackTrace()等接口用于获取堆栈跟踪数据等信息。 - Exception
Exception及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件。 - RuntimeException
RuntimeException是那些可能在 Java 虚拟机正常运行期间抛出的异常的超类。
编译器不会检查RuntimeException异常。例如,除数为零时,抛出ArithmeticException异常。RuntimeException是ArithmeticException的超类。当代码发生除数为零的情况时,倘若既"没有通过throws声明抛出ArithmeticException异常",也"没有通过try…catch…处理该异常",也能通过编译。这就是我们所说的"编译器不会检查RuntimeException异常"!
如果代码会产生RuntimeException异常,则需要通过修改代码进行避免。例如,若会发生除数为零的情况,则需要通过代码避免该情况的发生! - Error
和Exception一样,Error也是Throwable的子类。它用于指示合理的应用程序不应该试图捕获的严重问题,大多数这样的错误都是异常条件,和RuntimeException一样,编译器也不会检查Error。
三、异常结构
Java将可抛出(Throwable)的结构分为三种类型:被检查的异常(Checked Exception),运行时异常(RuntimeException)和错误(Error)。
- 运行时异常
定义: RuntimeException及其子类都被称为运行时异常。
特点: Java编译器不会检查它。也就是说,当程序中可能出现这类异常时,倘若既"没有通过throws声明抛出它",也"没有用 try-catch语句捕获它",还是会编译通过。例如,除数为零时产生的ArithmeticException异常,数组越界时产生的IndexOutOfBoundsException异常,fail-fail机制产生的ConcurrentModificationException异常等,都属于运行时异常。
虽然Java编译器不会检查运行时异常,但是我们也可以通过throws进行声明抛出,也可以通过try-catch对它进行捕获处理。
如果产生运行时异常,则需要通过修改代码来进行避免。例如,若会发生除数为零的情况,则需要通过代码避免该情况的发生! - 被检查的异常
定义: Exception类本身,以及Exception的子类中除了"运行时异常"之外的其它子类都属于被检查异常。
特点: Java编译器会检查它。此类异常,要么通过throws进行声明抛出,要么通过try-catch进行捕获处理,否则不能通过编译。例如,CloneNotSupportedException就属于被检查异常。当通过clone()接口去克隆一个对象,而该对象对应的类没有实现Cloneable接口,就会抛出CloneNotSupportedException异常。
被检查异常通常都是可以恢复的。 - 错误
定义: Error类及其子类。
特点: 和运行时异常一样,编译器也不会对错误进行检查。
当资源不足、约束失败、或是其它程序无法继续运行的条件发生时,就产生错误。程序本身无法修复这些错误的。例如,VirtualMachineError就属于错误。
按照Java惯例,我们是不应该是实现任何新的Error子类的! - unchecked异常和checked异常的区别
有了上面的认识之后,我们再来看什么是checked异常,什么是unchecked的异常。其实,Java语言规范对这两个定义十分简单,将派生于Error或者RuntimeException的异常称为unchecked异常,所有其他的异常成为checked异常。也就是说,如果出现了RuntimeException,就一定是程序员自身的问题。比如说,数组下标越界和访问空指针异常等等,只要你稍加留心这些异常都是在编码阶段可以避免的异常。如果你还是觉得这两个概念不好区分,那么“最暴力“的方法就是将常见的RuntimeException背下来,这样就可以省去很多判断的时间。
四、异常回滚
-
spring 的默认事务机制,当出现unchecked异常时候回滚,checked异常的时候不会回滚;
异常中unchecked异常包括error和runtime异常,需要try catch或向上抛出的异常为checked异常比如IOException,也就是说程序抛出runtime异常的时候才会进行回滚,其他异常不回滚,可以配置设置所有异常回滚:
当有try catch后捕获了异常,事务不会回滚,如果不得不在service层写try catch 需要catch后 throw new RuntimeException 让事务回滚;
Spring的AOP即声明式事务管理默认是针对unchecked exception回滚。也就是默认对R untimeException()异常或是其子类进行事务回滚;checked异常,即Exception可try{}捕获的不会回滚,如果使用try-catch捕获抛出的unchecked异常后没有在catch块中采用页面硬编码的方式使用spring api对事务做显式的回滚,则事务不会回滚, “将异常捕获,并且在catch块中不对事务做显式提交=生吞掉异常” ,要想捕获非运行时异常则需要如下配置:
解决办法:
1)在针对事务的类中抛出RuntimeException异常,而不是抛出Exception。
2)在txAdive中增加rollback-for,里面写自己的exception,例如自己写的exception:
<tx:advice id=‘txAdvice’ transaction-manager=‘transactionManager’>
tx:attributes
<tx:method name=’’ rollback-for=‘com.cn.untils.exception.XyzException’/>
</tx:attributes>
</tx:advice>
或者,定义不会滚的异常
<tx:advice id=‘txAdvice’>
tx:attributes
<tx:method name='update’ no-rollback-for=‘IOException’/>
<tx:method name=’*’/>
</tx:attributes>
</tx:advice> -
spring的事务边界是在调用业务方法之前开始的,业务方法执行完毕之后来执行commit or rollback(Spring默认取决于是否抛出runtime异常).
如果抛出runtime exception 并在你的业务方法中没有catch到的话,事务会回滚。
一般不需要在业务方法中catch异常,如果非要catch,在做完你想做的工作后(比如关闭文件等)一定要抛出runtime exception,否则spring会将你的操作commit,这样就会产生脏数据.所以你的catch代码是画蛇添足。
如:
try {
//bisiness logic code
} catch(Exception e) {
//handle the exception
}
由此可以推知,在spring中如果某个业务方法被一个 整个包裹起来,则这个业务方法也就等于脱离了spring事务的管理,因为没有任何异常会从业务方法中抛出!全被捕获并吞掉,导致spring异常抛出触发事务回滚策略失效。
不过,如果在catch代码块中采用页面硬编码的方式使用spring api对事务做显式的回滚,这样写也未尝不可。 -
基于注解的事务:
Transactional的异常控制,默认是Check Exception 不回滚,unCheck Exception回滚
如果配置了rollbackFor 和 noRollbackFor 且两个都是用同样的异常,那么遇到该异常,还是回滚
rollbackFor 和noRollbackFor 配置也许不会含盖所有异常,对于遗漏的按照Check Exception 不回滚,unCheck Exception回滚
》》》博主长期更新学习心得,推荐点赞关注!!!
》》》若有错误之处,请在评论区留言,谢谢!!!