Java中的异常
1、Java异常类层次结构图
Java中的异常是指一个在程序执行期间发生的事件,它中断正在执行程序的正常指令流,为了能够及时有效地处理程序中的运行错误,必须使用异常类。
在Java中,所有的异常都有一个共同的祖先,就是java.lang包下的Throwable类 。Throwable类有两个重要的子类:
- Exception:程序本身可以处理的异常,可以通过catch来进行捕获。Exception可以分为Checked Exception(受检查异常,必须处理)和 Unchecked Exception(不受检查异常,可以不处理)
- Error:Error属于程序无法处理的错误,不建议通过catch捕获。例如 Java虚拟机运行错误、虚拟机内存不够错误、类定义错误等 。这些异常发生时,Java虚拟机一般会选择线程终止
2、Checked Exception 和 Unchecked Exception有什么区别?
Checked Exception 即受检查异常。也叫作编译时异常
Java代码在编译过程中,如果受检查异常没有被catch或者throws关键字处理的话,就没办法通过编译。
像这样,IDEA会提示去try…catch或者throws出去。
除了RuntimeException及其子类以外,其他的Exception类及其子类都属于受检查异常 。常见的受检查异常有:IO相关的异常、ClassNotFoundException、SQLException
Unchecked Exception 即不受检查异常。也叫做运行时异常
Java代码在编译过程中,我们即使不处理不受检查异常也可以正常通过编译。
RuntimeException及其子类都统称为非受检查异常,常见的有:
- NullPointerException(空指针异常)
- IllegalArgumentException(参数错误比如方法入参类型错误)
- NumberFormatException(字符串转换为数字格式错误,IllegalArgumentException的子类)
- ArrayIndexOutOfBoundsException (数组越界错误)
- ClassCastException(类型转换错误)
- ArithmeticException(安全错误比如权限不够)
- UnsupportedOperationException(不支持的操作错误比如重复创建同一用户)
3、Throwable类常用方法有哪些
- String getMessage():返回异常发生时的简要描述
- String toString():返回异常发生时的详细信息
- String getLocalizedMessage():返回异常对象的本地化信息。
- void printStackTrace():在控制台上打印Throwable对象封装的异常信息
4、try-catch-finally如何使用?
- try块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块
- catch块:用于处理try捕获到的异常
- finally块:无论是否捕获或处理异常,finally块里的语句都会被执行 。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行
try {
System.out.println("Try to do something");
throw new RuntimeException("RuntimeException");
} catch (Exception e) {
System.out.println("Catch Exception -> " + e.getMessage());
} finally {
System.out.println("Finally");
}
不要在finally语句块中使用return 。当try语句和finally语句中都有return语句时,try语句块中的return语句会被忽略。这是因为,try语句中的return返回值会先被暂存在一个本地变量中,当执行到finally语句中的return之后,这个本地变量的值就变为了finally语句中的return返回值 。
5、finally中的代码一定会执行吗?
不一定。在某些特殊情况下,finally中的代码不会被执行
- finally之前虚拟机被终止运行的话,finally中的代码不会被执行
- 程序所在的线程死亡
- 关闭CPU
6、异常使用有哪些需要注意的?
- 不要把异常定义为静态变量,这样会导致异常栈信息错乱。每次手动抛出异常,我们都需要手动new一个异常对象抛出
- 抛出的异常信息一定要有意义
- 建议抛出更加具体的异常比如:字符串转换为数字格式错误的时候应该抛出NumberFormatException而不是其父类IllegalArgumentException
抛出更具体的异常是为了提供更多的关于问题的信息,帮助程序员更快地定位和解决问题 。抛出更具体的异常是良好的编程实践之一,可提高代码的可读性、可维护性和可靠性。
- 使用日志打印异常之后就不要再抛出异常了(两者不要同时存在一段代码逻辑中)
自定义异常
1、自定义异常
虽然Java中有很多异常类,但是在实际开发中所遇到的一些异常,不能完全表示。这时候就需要我们自定义异常类
- 定义一个取款超出余额时的异常
/**
* 自定义异常,取款超出余额时抛出异常
*/
public class AccountOverDrawnException extends Exception{
public AccountOverDrawnException() {
super();
}
public AccountOverDrawnException(String message) {
super(message);
}
@Override
public String toString(){
return getMessage();
}
}
- 在使用到的类中捕获这个异常
if (money > balance) {
try{
throw new AccountOverDrawnException("取款失败,账户余额不足");
}catch (AccountOverDrawnException e){
e.printStackTrace();
}
} else if (money < 0) {
System.out.println("取款失败,取款金额小于0");
} else {
//进行取款
...
}
}
2、SpringBoot中全局异常处理
创建一个全局异常管理
创建GlobalExceptionHandle.java
package com.xqh.exception;
import com.xqh.common.Result;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
@Slf4j
@RestControllerAdvice //捕获全局异常
public class GlobalExceptionHandler {
@ExceptionHandler(ServiceException.class)
@ResponseBody
public Result handle(ServiceException se){
return Result.error(se.getCode(),se.getMessage());
}
@ResponseStatus(HttpStatus.UNAUTHORIZED)
@ExceptionHandler(value = ShiroException.class)
public Result handler(ShiroException e) {
log.error("运行时异常:----------------{}", e);
return Result.fail(401, e.getMessage(), null);
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public Result handler(MethodArgumentNotValidException e) {
log.error("实体校验异常:----------------{}", e);
BindingResult bindingResult = e.getBindingResult();
ObjectError objectError = bindingResult.getAllErrors().stream().findFirst().get();
return Result.fail(objectError.getDefaultMessage());
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(value = IllegalArgumentException.class)
public Result handler(IllegalArgumentException e) {
log.error("Assert异常:----------------{}", e);
return Result.fail(e.getMessage());
}
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ExceptionHandler(value = RuntimeException.class)
public Result handler(RuntimeException e) {
log.error("运行时异常:----------------{}", e);
return Result.fail(e.getMessage());
}
}
上面我们捕捉了几个异常:
-
ServiceException: Service层抛出的异常
-
ShiroException:shiro 抛出的异常,比如没有权限,用户登录异常
-
IllegalArgumentException:处理 Assert 的异常
-
MethodArgumentNotValidException:处理实体校验的异常
-
RuntimeException:捕捉其他异常 运行时的异常
定义每一个异常类
如ServiceException.java
@Getter
public class ServiceException extends RuntimeException {
private String code;
public ServiceException(String code,String msg){
super(msg);
this.code=code;
}
}
使用全局异常类
//登录
public UserDTO login(UserDTO userDTO) {
User one = getUserInfo(userDTO);
if (one !=null){
BeanUtil.copyProperties(one,userDTO,true);
return userDTO;
}else {
throw new ServiceException(Constants.CODE_600,"用户名或密码错误"); //抛出异常
}
}
这样就完成了全局异常的处理。只要有需要自定义的异常,写出异常类,然后注册到GlobalExceptionHandle中。然后就可以全局使用这些异常了!