目录
引言
在现实的开发中总是充满了不良的数据和带有问题的代码,用户遇到错误就会感觉不爽,如果用户在运行程序期间,由于程序的错误或一些外部环境的影响造成用户数据的丢失,用户可能就再也不会使用这个程序了。为了避免这些事情的发生。至少做到下面几点:
- 向用户通报错误;
- 保存所有的工作结果;
- 允许用户以妥善的形式退出程序。
来点干货:项目开发过程中遇到的异常处理和事务回滚的问题
如果异常被try{}catch{}了,事务就不回滚了,如果想让事务回滚必须再往外抛try{}catch{throw new RuntimeException}。
在service类前加上@Transactional,声明这个service所有方法需要事务管理。每一个业务方法开始时都会打开一个事务。
Spring默认情况下会对运行期例外(RunTimeException)进行事务回滚。这个例外是unchecked
如果遇到checked意外就不回滚。
如何改变默认规则:
1 让checked例外也回滚:在整个方法前加上 @Transactional(rollbackFor=Exception.class)
2 让unchecked例外不回滚: @Transactional(notRollbackFor=RunTimeException.class)
3 不需要事务管理的(只查询的)方法:@Transactional(propagation=Propagation.NOT_SUPPORTED)
异常处理是什么
异常处理的任务就是将控制权从错误产生的地方转移给能够处理这种情况的错误处理器。研究程序中可能会出现的错误和问题,以及哪类问题需要关注:
- 用户输入错误 用户总是喜欢各行其是,或者出现各种输入的问题。
- 设备错误 硬件并不总是让他做什么就做什么,经常会出现问题,比如,忽然断电了,打印机卡纸了或者没纸了。
- 物理限制 磁盘满了,可用存储空间已被用完。
- 代码错误 下面详细介绍下。
背景:程序方法有可能无法正确执行,如:方法可能返回了一个错误的答案,或者错误地调用了其他的方法。计算的数组索引不合法,试图在散列表中查找一个不存在的记录,或者试图让一个空栈执行弹出操作。
对于方法中的一个错误,传统的做法是返回一个特殊的错误码。有可能无法明确地将有效数据与无效数据加以区分。一个返回整数的方法就不能简单的返回-1表示错误,因为-1很可能是一个合法的结果。在java中,某个方法不能采用正常的途径完成它的任务,就可以通过另外的一个途径退出方法。在这种情况下,方法并不返回任何值,而是抛出一个封装了错误信息的对象。
需要注意的是,这个方法将会立刻退出,并不返回任何值。此外,调用这个方法的代码也将无法继续执行,取而代之的是,异常处理机制开始搜索能够处理这种异常状况的异常处理器。
异常分类
所有的异常都是由 Throwable 继承而来,用户也可以创建自己的异常类。分解为两个分支: Error (结果:进程无法正常运行。项目无法正常启动或者宕机。)和 Exception(结果:本次调用的功能会不可用,其他的功能正常。):
Error 类层次结构描述了 Java 运行时系统的内部错误和资源耗尽错误。 应用程序不应该抛出这种类型的对象。 如果出现了这样的内部错误, 除了通告给用户,并尽力使程序安全地终止之外, 再也无能为力了。这种情况很少出现。
在设计 Java 程序时
需要关注 Exception 层次结构。 这个层次结构又分解为两个分支划分两个分支的规则是: 由程序错误导致的异常属于 RuntimeException ; 而程序本身没有问题, 但由于像 I/O 错误这类问题导致的异常属于其他异常:
常见运行时异常:
常见非运行时异常:
不是派生于 RuntimeException 的异常包括:
•试图在文件尾部后面读取数据。
•试图打开一个不存在的文件。 因为:“ 是否存在” 取决于环境, 而不只是取决于你的代码。
•试图根据给定的字符串查找 Class 对象, 而这个字符串表示的类并不存在 。
Java 语 言 规 范 将 派 生 于 Error 类 或 RuntimeException 类的所有异常称为非受查( unchecked ) 异常, 所有其他的异常称为受查( checked) 异常。这是两个很有用的术语。 编译器将核查是否为所有的受査异常提供了异常处理器。
注意:开发过程中, Error:导致编译出错,项目进程中断。checked异常:抛出异常后会中断线程,导致部分功能没办法启用
实现异常的声明和捕获
引言:一个方法应该告诉不仅仅告诉返回是什么,同时还要告诉编译器可能会发生的错误。
//下面是标准库中提供的FileInputStram类的一个构造器的声明
public FilelnputStream(String name) throws FileNotFoundException
这个声明表示这个构造器将根据给定的 String 参数产生一个 FilelnputStream 对象,但也有可能抛出一个 FileNotFoundExc印tion 异常。如果发生了这种糟糕情况, 构造器将不会初始化一个新的 FileInputStream对象, 而是抛出一个 FileNotFoundException 类对象。 如果这个方法真的抛出了这样一个异常对象,运行时系统就会开始搜索异常处理器, 以便知道如何处理FileNotFoundException 对象 。
记住在遇到下面 4 种情况时应该抛出异常:
1 ) 调用一个抛出checked方法, 例如, FilelnputStream 构造器。
2 ) 程序运行过程中发现错误, 并且利用 throw语句抛出一个受查异常checked(下一节将详细地介绍 throw 语句)。
3 ) 程序出现错误, 例如,a[-l]=0 会抛出一个 ArraylndexOutOffloundsException 这样的非受查异常unchecked。
4 ) Java 虚拟机和运行时库出现的内部错误。
如果出现前两种情况之一, 则必须告诉调用这个方法的程序员有可能抛出异常。 因为任何一个抛出异常的方法都有可能是一个死亡陷阱。 如果没有处理器捕获这个异常,当前执行的线程就会结束。对于那些可能被他人使用的 Java 方法, 应该根据异常规范( exception specification), 在方法的首部声明这个方法可能抛出的异常。
/*对于那些可能被他人使用的 Java 方法, 应该根据异常规范( exception specification), 在方法的首部声明这个方法可能抛出的异常。*/
class HyAni
{
public Image loadlmage(String s) throws IOException{
}
}
如果一个方法有可能抛出多个受查异常类型, 那么就必须在方法的首部列出所有的异常类。每个异常类之间用逗号隔开。 如下面这个例子所示:
class MyAnimation
{
public Image loadlmage(String s) throws FileNotFoundException, EOFException
}
一个方法必须声明所有可能抛出的受查异常, 而非受查异常要么不可控制(Error),要么就应该避免发生 ( RuntimeException )。 如果方法没有声明所有可能发生的受查异常, 编译器就会发出一个错误消息。一般在开发的过程就是遵循“内部抛出,外部捕获”
警告: 如果在子类中覆盖了超类的一个方法, 子类方法中声明的受查异常不能比超类方法中声明的异常更通用 (也就是说, 子类方法中可以抛出更特定的异常, 或者根本不抛出任何异常)。特别需要说明的是, 如果超类方法没有抛出任何受查异常, 子类也不能抛出任何受查异常。
如何判断用checked异常还是unchecked异常
总结 摘选自 https://blog.csdn.net/kingzone_2008/article/details/8535287的项目文档总结
我过去支持checked异常,但是最近我改变了我的观点。Rod Johnson(Spring Framework),Anders Hejlsberg(C#之父),Joshua Bloch(Effective Java,条目41:避免checked异常的不必要的使用)和其他一些朋友使我重新考虑了checked异常的真实价值。最近我们尝试在一个较大的项目中使用unchecked异常,效果还不错。错误处理被集中在了少数几个类中。会有需要本地错误处理的地方,而不是将异常传播给主错误处理代码。但是这种地方不会很多。由于代码中不会到处都是try-catch块,我们的代码变得可读性更好。换句话说,使用unchecked异常比使用checked异常减少了无用的catch-rethrow try-catch块。总之,我建议使用unchecked异常。至少在一个工程中尝试过。我总结了以下原因:
- Unchecked异常不会使代码显得杂乱,因为其避免了不必要的try-catch块。
- Unchecked异常不会因为异常声明聚集使方法声明显得杂乱。
- 关于容易忘记处理unchecked异常的观点在我的实践中没有发生。
- 关于无法获知如何处理未声明异常的观点在我的实践中没有发生。
- Unchecked异常避免了版本问题。