异常的分类结构图
异常机制是为了解决什么问题?
它也是线程结束的一种方式,从某中角度来讲,它与正常return没有什么区别,只不过是一种异常的方式结束。那为什么需要这种异常的机制呢?异常机制本身也是划分了严重程度,如:Error/Exception. 它以一种不侵入正常流程编码的形式,尽量不让程序崩溃(Error类型的异常), 同时给开发者友好的提示信息(方便问题的定位)。
各种异常在什么场景下出现?能否处理?
Error错误
这种异常通常都是程序级别的错误,直接会引起程序崩溃。
常见的异常有:VirtualMachineError(jvm错误)、AWTError、OutOfMemoryError(堆内存溢出)、StackOverflowError(栈内存溢出)、NoClassDefFoundError、NoSuchMethodError。
这种错误通常程序本身无法处理,需要对JVM优化处理,如:参数大小设置、引入依赖包等。
RuntimeException运行时异常
这种异常通常都是在程序运行的过程中出现的异常。要么是BUG,要么是参数错误,而这一切的异常情况在编码的时候根本无法预料。会让单个方法的调用直接异常结束.
常见的异常有:IllegalArgumentException(不合法参数异常)、IllegalAccessException(不合法的访问)、ClassCastException(类型转换异常)、IndexOutOfBoundsException(边界异常)、NumberFormatException(数字格式异常).
运行时异常通常有传入的参数错误或程序BUG引起,这种异常需要用户或开发者根据提示信息来修改问题。
CheckedException检查型异常
非运行时异常,是在编码时显示定义的异常,这种异常必须捕获(try catch)或抛出(throws),否则编译时无法通过。
常见的异常有:IOException(IO异常,如:SocketException网络异常,文件流异常)、SQLException(sql异常).
检查型异常,通常都是可预见的异常情况,而在编码的时候进行的一种补救措施。
根据什么来设计异常?
根据异常类型来设计
Error错误类型的异常,通常都属于系统级别的问题. 除非我们要去终止整个进程,否则我们一般都不会涉及到此类的异常设计;
CheckedException可检查型异常,属于应用编码级别的问题.它属于一种备用手段,比如:SocketException/FileException,捕获之后,还可以进一步的补救.我认为它属于一个另类的编程思想.
RuntimeException运行时异常,属于请求级别/一次方法调用的问题. 在程序处理的时候,总会有想不周全的问题.比如:用户传入的参数不正确;开发者的BUG;这种问题通常都会中止此次请求或方法调用,返回错误提示信息,然后再由对应的人员去解决.
根据谁造成的问题谁来解决的原则
引起异常问题的原因无非两种:客户和开发者.
当我们的应用内存不够或磁盘空间不足或系统宕机,这种出现系统级别的错误. 只能由运维或开发者来解决,对于客户来说并不可见,也并不关心.
当我们的应用某个方法中的片段代码,因为开发者的疏忽,而非造成的BUG.这种问题只能由开发者来解决,对于客户来说并不可见,也不关心.
当客户调用某个方法时,传入的参数不正确或数据不正确,这种问题只能由客户来解决,而开发者只需要友好的提示给客户.
异常的设计样例
/** * 程序BUG出现的异常.用于开发者定位问题所在. * @author yangyc */public class AppException extends RuntimeException{ public AppException(String message, Throwable cause) { super(message, cause); }}/** * 提示性异常.由调用者来解决.如果参数为空,参数格式不正确,数据获取不到等. * @author yangyc */public class PromptException extends RuntimeException { public PromptException(String message, Throwable cause) { super(message, cause); }}/** * 远程调用服务的异常.这种在编码期就可以预料到的问题. * @author yangyc */public class RpcException extends Exception { public RpcException(String message, Throwable cause) { super(message, cause); }}
使用异常技巧
1.什么时候使用检查型异常或运行时异常?
异常可预见或可补救的情况,采用检查型异常,从编码的角度增强程序的健壮性. 相反无法预料, 问题出现就必须中止的问题,采用运行时异常.
2.异常什么时候捕获?
通常在调用起点捕获或AOP的更上层统一处理,中间不要去捕获; 明确知道问题并可以对此类异常处理,则捕获,而这种类型的异常,通常都会进行异常转换,继续往上层抛出.
3.异常捕获时,try catch的内容应该是多少?
一般不宜将捕获的代码太长,会影响异常处理机制的复杂度;同时,也应考虑将紧密代码放在一起,保证代码的可读性.
4.异常的转换
有些时候,捕获异常之后,会抛出一个新异常,继续由后续程序处理.这时,就需要进行异常的转换.比如:将检查型异常转换成运行时异常.
5.异常的传递
当出现异常转换时,要注意原始异常信息不丢失.应该使用 new XxxException(msg, e)这个方法.
6.尽早抛出异常,尽晚的捕获异常,明确的抛出异常类型
7.捕获异常后,不要忽略异常
public void doNotIgnoreExceptions() { try { // do something } catch (NumberFormatException e) { // this will never happen }}
8.不要捕获Throwable
Throwable是所有异常(Exception)和错误(Error)的父类,虽然它能在catch从句中使用,但永远都不要这样做!如果你在catch从句中使用了Throwable,它将不仅捕获所有异常,它还将捕获所有错误,错误是由JVM抛出的,用来表明不打算让应用来处理的严重错误。OutOfMemoryError和StackOverflowError便是典型的例子,它们都是由于一些超出应用处理范围的情况导致的。
共同学习,写下你的评论
评论加载中...
作者其他优质文章