文章目录
- error和exception的异同
- 相同点
- 不同点
- 简述exception
- 异常的处理方式
- try-catch
- throw和throws
- throw
- throws
- checkedException
- uncheckedException
- 自定义Exception
我们在写代码的过程中,会不可避免的出现各种错误以及异常,所以了解错误和异常的体系对于开发人员来说还是挺重要的,接下来我带大家看一下java中的error和exception。
在开始前我们先来看张图,包含了exception和error的各自两种分类,从左到右分别为直接继承Exception的checkedException(编译时异常)、继承RuntimeException的uncheckedException(运行时异常)、继承VirtualMachineError的虚拟机错误、直接继承Error的错误。
error和exception的异同
相同点
error和exception都继承了Throwable类,通过源码可以看出error和exception类中只有构造方法,且结构相同。
不同点
error无法通过代码来被抛出或捕获,当程序在运行中出现error的时候,就意味着程序即将停止运行了。而exception是可以被代码捕获或抛出的,也就是说我们可以通过捕获或抛出来将异常对程序的影响降到最低,让代码按照设定好的逻辑来处理异常,让程序尽可能恢复正常并继续执行。
简述exception
通过上述的内容,我们可以得知,在java中error不能被处理,而exception可以处理,这就是说在写代码的时候,我们可以忽略error将更多的精力放在对exception的处理上。
exception分为两种,一种是(checked)可检查异常也叫编译时异常,另一种是(unchecked)不可检查异也叫运行时异常。
异常的处理方式
面对异常最重要的就两个问题:这个异常该交给谁来处理;这个异常该如何处理。jdk提供了两种方式来处理异常:捕获和抛出。捕获就是try-catch说明要自己处理异常,如何处理则需要结合实际情况来判断。抛出又分两种分别是throw和throws,则需要调用者处理。异常交给谁来处理是有约定俗成的规则的,在说明规则前需要简单了解下三层架构(视图层、业务逻辑层、数据访问层),视图层只负责接收用户输入的数据和显示处理后用户需要的数据,数据访问层只负责将业务逻辑层处理过的数据持久化到数据库,那现在就只剩下业务逻辑层来处理异常,但通常业务逻辑层需要控制数据库事务,如果捕获异常的话,就需要自己在catch中手动控制事务,为了使代码更加优雅,我们选择将异常抛出在视图层使用全局异常处理机制来处理异常。关于try-catch、throw和throws处理异常的注意点下面结合伪代码来简要说明。
try-catch
try-catch见名知意就是尝试-捕获很好理解,try代码块中的代码可能会出错,如果代码出错则被catch捕获并执行catch代码块中的逻辑,同时代码并不会停止运行,catch可以存在多个以针对不同异常做出不同的处理。catch代码块后可追加finally代码块,finally中的代码在任何时候都会执行,无论try中代码有没有异常,通常在finally代码块中进行一些资源的关闭,例如数据库连接的关闭、io流的关闭等。
public void methodDemo(){
try {
从数据库中查询数据
将查询出的数据写入文件
}catch (SQLException e){
logger.error("查询数据出错",e);
}catch (IOException e){
logger.error("写入文件出错",e);
}finally {
databaseConnection.close();
ioStream.close();
}
}
throw和throws
throw和throw都是将异常抛出,如果代码出现异常都将由调用者处理,但两者还是有区别的
throw
- 写在方法体内,抛出的是Exception或Error子类的实例
- 只能抛出单个异常
- 使用throw抛出异常就说明异常一定存在
throws
- 写在方法名后,抛出的是Exception或Error的子类
- 可以抛出多个异常,用逗号隔开
- 使用throws抛出异常说明异常可能存在
checkedException
一般来说直接继承exception的都是编译时异常,在编写代码的时候,ide会提示你进行try-catch或throws处理,常见的有IOException、SqlException等。如果throw或throws抛出则交给调用者处理,当使用try-catch时,就说明我们预料到可能会发生发生某个异常或某几个异常,同时对这些异常做一些针对性的处理。
public void sqlMethod() {
try{
业务逻辑1
执行sql
业务逻辑2//可能出错的逻辑
}catch (Exception e){
e.printStackTrace();
}
}
这是很典型的一个场景,将业务代码放在try中,如果在处理业务逻辑时报错,就回滚事务。但如果仔细观察就会发现,上述的写法存在几个问题。下面来一一说明
- try-catch是消耗资源的,所以要尽可能减少try代码块中的代码,只在try代码块中书写必要的代码,所以该写成
public void sqlMethod() {
业务逻辑1
执行sql
try{
业务逻辑2//可能出错的逻辑
}catch (Exception e){
e.printStackTrace();
}
}
- catch中处理异常时,应尽可能的精准。try-catch的意义就在于让我们可以捕获特定的异常并进行针对性的处理,如果直接catch-exception就和处理异常的理念背道而驰了,同时编译时异常都为exception的子类,在程序执行时会去遍历寻找符合的异常,会消耗更多的资源,因此严禁直接catch-exception
public void sqlMethod() {
业务逻辑1
执行sql
try{
业务逻辑2//可能出错的逻辑
}catch (SQLException e){
e.printStackTrace();
}
}
- 严禁生吞异常,只有对异常进行针对性的处理,捕获才是有意义的,如果不处理请throw或throws抛出交给调用者处理
public void sqlMethod() {
业务逻辑1
执行sql
try{
业务逻辑2//可能出错的逻辑
}catch (SQLException e){
e.printStackTrace();
回滚事务
}
}
- 严禁使用e.printStackTrace()输出异常,如需打印异常信息请使用logger.error输出到控制台。printStackTrace输出字符串到控制台是基于System.err(标准出错)即时输出,而System.out(标准输出)是由缓存的,所以当二者同时使用时输出顺序可能不是真实的输出顺序,会增加定位问题的难度。此外更重要的是,字符串输出在控制台需要字符串常量池所在的内存块有足够的空间,而printStackTrace输出的是堆栈信息一般都很长,会占用不必要的空间甚至锁死导致系统停止运行。基于以上两点在项目中严禁使用e.printStackTrace()输出异常
public void sqlMethod() {
业务逻辑1
执行sql
try{
业务逻辑2//可能出错的逻辑
}catch (SQLException e){
logger.error("提示信息", e);
}
}
uncheckedException
运行时异常,一般都是逻辑错误,需要程序员在编码过程中规避,处理方式常见的有:对参数格式的校验、对集合和数组长度的判断、判空处理等。以下代码毫无疑问会抛出IndexOutOfBoundsException
public static void runtimeExceptionMethod() {
ArrayList<Object> list = new ArrayList<>();
System.out.println(list.get(0));
}
为了避免这种情况我们需要对list的长度进行判断,当list长度大于0时才去输出list的第一个元素
public static void runtimeExceptionMethod() {
ArrayList<Object> list = new ArrayList<>();
if(list.size() > 0){
System.out.println(list.get(0));
}
}
自定义Exception
除了jdk自带的两大种异常外,jdk还允许我们自行继承Exception和Error类来实现自定义异常和自定义错误,上面已经说过了error我们不去考虑,接下来简述下自定义异常。其实对于运行时异常,我们有着进阶的处理方式就是抛出自定义异常。
public class MyException extends RuntimeException {
public MyException(String message) {
super(message);
}
public MyException(int code, String message) {
this(code, message, (Throwable)null);
}
public MyException(int code, String message, Throwable cause) {
super(code, message, cause);
}
}
public static void runtimeExceptionMethod() {
ArrayList<Object> list = new ArrayList<>();
if(list.size() > 0){
System.out.println(list.get(0));
}else{
throw new MyException("500","集合的长度不合法");
}
}