一、什么是Java异常
在Java中,异常是程序运行过程中出现的错误。根据错误的严重程度,Java将异常分为两类:Error(错误)和Exception(异常)。
-
Error(错误):
- 这些是严重的问题,如内存溢出或JVM(Java虚拟机)故障。这类错误一般无法由程序自行处理,只能通过系统干预。
-
Exception(异常):
- 这是在程序运行中可以预料到的问题,如空指针异常或数组越界。这些异常可以被程序捕获并处理,避免程序崩溃。
1.1 编译异常和运行时异常
-
编译异常(受检异常):
- 这是在编译阶段必须处理的异常,如文件未找到或数据库连接失败。Java要求你必须处理这些异常,确保程序的健壮性。
-
运行时异常(非受检异常):
- 这是在程序运行期间可能发生的异常,如空指针异常或算术异常。这类异常通常由代码错误引起,不需要强制捕获。
二、处理异常的方式
在Java中,我们通常使用try-catch块来捕获和处理异常。
try {
// 业务逻辑
} catch (Exception e) {
// 捕获异常后的处理逻辑
}
你可以根据不同类型的异常,进行更细致的捕获:
try {
// 业务逻辑
} catch (IOException ie) {
// 捕获IO异常的处理逻辑
} catch (Exception e) {
// 捕获其他异常的处理逻辑
}
三、throw 抛出异常
我们可以使用throw语句主动抛出异常来控制程序的执行流程:
public UserVO queryUser(Long id) {
UserDO userDO = userMapper.queryUserById(id);
if (userDO == null) {
throw new RuntimeException("用户不存在");
}
return userDO.toVo();
}
为更精确地表示不同的错误类型,我们可以自定义异常类:
public UserVO queryUser(Long id) {
UserDO userDO = userMapper.queryUserById(id);
if (userDO == null) {
throw new UserNotFoundException();
}
if (!checkLicence(userDO)) {
throw new BadLicenceException();
}
return userDO.toVo();
}
四、如何优雅地抛出异常
4.1 使用断言增加代码的可读性
我们可以使用断言(assert)来替代简单的if-throw结构:
public UserVO queryUser(Long id) {
UserDO userDO = userMapper.queryUserById(id);
Assert.notNull(userDO, "用户不存在");
return userDO.toVo();
}
4.2 自定义断言
通过自定义断言,我们可以在断言失败时抛出自定义异常:
- 自定义异常类:
public class BusinessException extends BaseException {
public BusinessException(IResponseEnum responseEnum, Object[] args, String msg) {
super(msg, responseEnum, args);
}
}
- 自定义断言接口:
public interface BusinessExceptionAssert extends IResponseEnum, MyAssert {
@Override
default BaseException newException(Object... args) {
return new BusinessException(this, args, this.getMsg());
}
}
- 使用枚举定义异常类型:
public enum ResponseEnum implements IResponseEnum, BusinessExceptionAssert {
USER_NOT_FOUND("1020", "用户不存在");
private final String code;
private final String msg;
ResponseEnum(String code, String msg) {
this.code = code;
this.msg = msg;
}
@Override
public String getCode() {
return code;
}
@Override
public String getMsg() {
return msg;
}
}
- 使用自定义断言:
public UserVO queryUser(Long id) {
UserDO userDO = userMapper.queryUserById(id);
ResponseEnum.USER_NOT_FOUND.assertNotNull(userDO);
return userDO.toVo();
}
五、统一处理异常
在代码的网关处,我们可以统一处理异常,确保系统的稳定性:
@ControllerAdvice
public class BusinessExceptionHandler {
@ExceptionHandler(value = BusinessException.class)
@ResponseBody
public Response handleBusinessException(BaseException e) {
return new Response(e.getResponseEnum().getCode(), e.getResponseEnum().getMsg());
}
}
这种方式结合了自定义断言的高可读性和自定义异常的精确性,使得代码更简洁易读,同时提高了异常处理的灵活性。