Java异常
首先程序错误分为三种:
- 编译错误:一般语法规则,编译器会给提示
- 运行时错误:程序运行期间产生的错误,如JVM的OOM错误
- 逻辑错误:比如写了
1/0
等
继承结构
首先异常的父类是Throwable
,然后两个子类,Error
和Exception
。其中Error(错误)是JVM中系统的异常,这些错误是不可控的,一般程序出现死循环或者线程死锁之类会导致出现此类错误。
然后Exception
中又分为两大类:运行时异常(RuntimeException
)、非运行时异常(IO异常、SQL异常
等)。运行时异常
这种系统异常可以处理也可以不处理,所以编译器不强制用try…catch处理或用throws声明。
但是非运行时异常(又称编译异常),如IOException等,这种异常必须要显式的解决或抛出,要么自己try-catch解决,要么把异常抛出去交给jvm来解决,无论如何必须要处理这种异常。编译器会强制让我们进行处理的
。
那么可以得知:首先Error中的错误
是我们无法手动处理的,只能通过优化代码等方式调整,然后Exception中的RuntimeException
是可以不进行try-catch或throws处理的,那么某一层面上可以说这两种错误是非受查异常
,也就是编译器不会进行检查的异常。
而Exception中的IOException等
是编译器一定会进行检查且必须让我们手动处理(解决异常或抛出异常给jvm)被称之为受查异常
。
如何处理异常
运行时异常(系统异常)
不需要预处理,通过规范的代码可以避免产生这种异常
受检异常(编译时异常)
必须预处理,否则编译报错,有两种处理方式 :
- 捕获处理
- 抛出异常
处理异常
try {
} catch (OneException e) {
} catch (TwoException e) {
} finally {
}
- try中包含了可能产生异常的代码
- try后面是catch,catch可以有一个或多个,catch中是需要捕获的异常
- 当try中的代码出现异常时,出现异常下面的代码不会执行,马上会跳转到相应的catch语句块中,如果没有异常不会跳转到catch中
- finally表示,不管是出现异常,还是没有出现异常,finally里的代码都执行,finally和catch可以分开使用,但finally必须和try一块使用
而且要注意:假如有多个异常需要进行捕获,异常的捕获顺序是先子类后父类
的,如下:
try {
} catch (ChildException e) {
} catch (ParentException e) {
} finally {
}
抛出异常
在定义方法时,如果方法体中有受检(编译时)异常需要预处理,可以捕获处理
,也可以抛出处理
。
处理异常时,使用throws
抛出处理:
- 谁调用这个方法,谁负责处理该异常
- 在定义方法时,把异常抛出就是为了提醒方法的使用者,有异常需要预处理
在处理异常时,是选择捕获处理还是抛出处理
- 一般情况下,在调用其他方法时,如果被调用的方法有受检(编译时)异常需要预处理,选择捕获处理,因为你调用了方法, 你负责处理该异常。
- 在定义方法时,如果方法体中有受检异常需要预处理,可以选择捕获 ,也可以选择抛出处理。如果方法体中通过throw语句抛出了一个异常对象,所在的方法应该使用throws声明该异常。
// 声明方法时抛出异常,并且声明了一个异常,告知调用者
public void show() throws Exception{
throw new RuntimeException();
}
// 调用者需要继续 处理异常 或者 抛出异常
public void test() throws Exception {
show();
}
父子类中的异常
子类中声明的异常的范围不能超过父类声明的异常范围
:
- 父类中方法如果没有声明异常,那么子类重写此方法时也无法抛出异常,否则会编译报错
- 同时如果父类方法声明了一个类型的异常,那么子类中可抛出的异常必须是
<=
父类方法抛出的异常类型的。
如下错误实例:
public class A {
// 父类抛出的IOException是Exception的子类
public void show() throws IOException{
}
}
class B extends A {
// 这个方法会编译报错,不能抛出比父类方法范围大的异常
@Override
public void show() throws Exception {
}
}
自定义异常
为了封装异常和快速定位异常,我们可以自定义异常。
自定义异常通常继承于Exception
或RuntimeException
,到底继承那个应该看具体情况来定。
自定义异常类可以有自己的变量和方法来传递错误代码
或传递其它异常相关信息
。实际工作中,都会自定义异常类来处理异常。
自定义非受查异常
class CheckedException extends Exception {
public CheckedException() {
// 调用父类的默认构造函数
super();
}
public CheckedException(String message) {
// 手动调用父类的构造方法
super(message);
}
}
自定义受查异常(常用)
class BusinessException extends RuntimeException {
public BusinessException() {
// 调用父类的默认构造函数
super();
}
public BusinessException(String message) {
// 手动调用父类的构造方法
super(message);
}
}
手动抛出异常(伪代码)。如在springboot项目中我们配置好全局捕获异常的增强后,可以直接在程序中抛出异常。如下涉及到一些业务逻辑。
Order o = orderService.getById(5);
if(Objects.isNull(o)) {
throw new BusinessException("订单为空了");
}
抛出异常后,会被全局的捕获异常增强捕获,然后返回通用的数据格式给客户端。
总结
-
异常的分类
-
两种异常分类的区别
-
异常的5个关键字,
try
、catch
、finally
、throws
、throw
-
处理异常:捕获处理、抛出交给调用者处理
-
异常捕获顺序,先捕获子类异常,再去捕获父类异常
-
父子继承关系中方法重写要注意的,子类抛出异常的范围不能大于父类异常范围
SpringBoot中如何处理异常
在Java程序中我们知道:程序中可能出现的非受查异常或受查异常我们都可已进行处理(不包括Error错误),如使用try-catch进行捕获处理,或者直接抛出异常,让调用者处理。
在SpringBoot项目中,由于要给客户端(前端)返回各种各样的数据,这些数据通常都是需要规定统一的数据格式进行返回,而当涉及到程序异常时,如何给客户端返回一些提示信息?
定义一个异常类
@Data
public class BusinessException extends RuntimeException{
private static final long serialVersionUID = 1L;
private String msg;
private int code = 500;
public BusinessException(String msg) {
super(msg);
this.msg = msg;
}
public BusinessException(String msg, Throwable e) {
super(msg, e);
this.msg = msg;
}
public BusinessException(String msg, int code) {
super(msg);
this.msg = msg;
this.code = code;
}
public BusinessException(String msg, int code, Throwable e) {
super(msg, e);
this.msg = msg;
this.code = code;
}
}
SpringBoot配置全局异常捕获
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public R handleException(Exception e, HttpServletRequest request) {
log.error(request.getRequestURI() + ":服务运行异常------> {}", e);
return R.buildFail("服务运行异常" + e.getMessage());
}
/**
* 自定义异常处理
*/
@ExceptionHandler(BusinessException.class)
public R handleException(BusinessException e, HttpServletRequest request) {
log.error(request.getRequestURI() + ":自定义内部异常------> {}", e);
return R.buildFail(e.getMsg());
}
}
在开发过程中,如果程序出现异常那么会报错抛出异常,那么我们直接用这个全局异常捕获到后,直接返回给客户端一个提示即可。
实际上在开发中,我们都是自定义RuntimeException
的子类异常(非受查异常),然后加以运用完善异常的处理机制。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/48389.html