0. 异常体系

-
Throwable
java所有的异常和错误类都继承自Throwable
类,其有两个直接子类:Error
和Exception
。 -
Error
其中Erro
r是系统性异常,是无法恢复的,也不应该捕获的,日常代码不会使用到的,比如内存溢出错误OutOfMemoryError
。 -
Exception
Exception
的子类分为两类:RuntimeException
和RuntimeException
子类,Exception的其他子类,分别称为受检异常和非受检异常。 - checked exception
受检异常是Exception
类和直接或间接继承自Exception
的子类,但不继承RuntimeException
。
受检异常是必须要处理的异常,也就是必须要try-catch捕获或者声明throws抛出的异常,如:
ClassNotFoundException
IOException
SQLException
- unchecked exception
非受检异常,也成为运行时异常,直接或间接继承自RuntimeException
,有别于受检异常的一个重要的特定是,非受检异常不需要try-catch捕获或者throws声明来处理,如:
NullPointerException
IllegalArgumentException
NumberFormatException
IndexOutOfBoundsException
IllegalStateException
1. 使用非受检异常
由于调用受有检异常声明的api,必须要捕获或者声明throws,若throws,则更上层的调用者还是一样要处理,所以如果能确定需要调用者意识到采取措施,并且能有恢复方案才考虑使用受检异常,否则如果都不确定,就应该使用非受检异常。
2. 使用标准异常
使用jdk标准的异常,因为一个异常类主要的信息类名,而标准异常是大家熟知的,能到达见名知意,在排除问题时能很好的定位。
3. 自定义异常
如果非要自定义异常,则可以按业务分层设计对应的异常体系,比如WEB应用,有view、controller、service、dao层,可以设计对应的异常类,并建立对应message和code:
BaseException extends RuntimeException // 统一业务处理逻辑,子类只有类名不同
BussinessException extends BaseException
ViewException extends BaseException
ControllerException extends BaseException
ServiceException extends BaseException
DAOException extends BaseException
4. 没有异常恢复方案时不要捕获异常
如果对异常情况没有恢复方案,则不要捕获异常并抛出,因为捕获并抛出异常性能也会影响,并且输出大量重复的异常信息,这样不便于定位错误。
5. 异常转义不要抛弃原始异常
比如DAO层捕获IOException
时,可以转义为DAOException
异常,但不能抛弃到原始的IOException
。
6. 不要抛弃异常
如果已有恢复方案,则可以考虑吃掉异常,并转到恢复方案,同时记录异常信息,比如在使用redis时,redis连接失败,则可以转调DB获取数据。
public void discardException() {
try {
FileInputStream input = new FileInputStream("data.txt");
}
catch (IOException e) {
// 不应该抛弃异常, 除非已经确定有恢复方案
e.printStackTrace();
}
}
7. 使用和捕获具体异常
8. 不要使用异常来控制程序流程
9. 输出详细的异常信息
捕获异常后,应该输出完整的上下文信息,比如请求远程接口失败,则可以将URL、入参等相关信息都输出,以便定位排查。
10. 统一规范的输出异常信息
11. 使用finally关闭资源或者try-catch-resouces
我们在使用IO、数据库链接需要在finally块或者try-catch-resources语句关闭资源。
但如果使用try-catch-resouces语句时,若close资源异常,则会影响整体业务的,所以可以考虑使用finally块close资源,并且压制异常,如:
public class ReadFile {
public void read(String filename) throws BaseException {
FileInputStream input = null;
IOException readException = null;
try {
input = new FileInputStream(filename);
} catch (IOException ex) {
readException = ex;
} finally {
if (input != null) {
try {
input.close();
} catch (IOException ex) {
if (readException == null) {
ex.printStackTrace(); // 打印异常
}else{
readException.addSuppressed(ex);
}
}
}
if (readException != null) {
throw new BaseException(readException);
}
}
}
}
12. 区分异常和错误提示
当程序出现异常或者错误时,应该详细记录异常日志,以便排除,但这些日志不应直接展示给用户,展示给用户的应该是重新输入、输入的数据格式不对,如果是系统异常,可以提示网络异常。
网友评论