关于java异常
一般来说,当程序遇到错误,应该尽量处理错误,然后按照正确的姿势退出程序。否则,你可以想象一下当客户使用系统的时候,突然系统异常,直接报了一堆用户不知道的代码提示,这是多么难堪的局面。
一、异常的结构
异常有一个共同的祖先Throwable,其关系结构如下
如图所见,Throwable有两个重要的子类,Error和Exception。
-
Error
Error代表的错误是程序无法处理的,这表示程序中出现了严重的问题,比如OutOfMemoryError、ThreadDeath,当出现这些错误,JVM一般会选择线程终止,因为他们超出了应用程序控制和处理的能力之外。
-
Exception
Exception代表的异常是程序可以处理的。Exception一般分为RuntimeException及其子类和此外的Exception及其子类两部分。运行时异常RuntimeException这部分,比如NullPointerException,这些异常是不可查异常,也就是程序中可以选择捕获处理,也可以不处理;非运行时异常这部分比如SQLException是可查异常,也即是必须捕获处理或者向上抛出。
-
注:
a. 可查异常checked exceptions
是编译器要求必须处理的异常,要么用try-catch捕获处理,要么向上抛出,否则编译不通过。这些异常包括了除了RuntimeException和RuntimeException子类以外的这部分Exception。
b. 不可查异常unchecked excetions
是编译器不要求处理的异常,包括运行时异常RuntimeException及其子类和Error。
二、异常处理
-
假如通过throw语句抛出异常,则该方法应该同时使用throws子句声明抛出的异常类型。如
public void test() throws RuntimeException { throw new MyRuntimeException(); }
假如方法没有显示使用throw抛出异常,但是仍然可能抛出异常,也可以使用throws子句声明抛出异常的类型。
-
假如一个方法使用throws抛出异常,则方法的调用者必须检查处理异常或者向上抛出。
-
如果是不可查异常,可以不适用throws子句声明抛出异常,也能通过编译,但是出现异常时仍然会被系统抛出。
-
假如是可查异常,那么必须使用try-catch捕获处理或者继续向上抛出,否则不能通过编译。
-
假如方法覆盖,则必须抛出和被覆盖方法抛出的异常是同一个异常或其子类异常。
-
假如所有方法都层层向上抛出,最终会由JVM处理,JVM将会打印异常消息和堆栈信息。
三、当发生异常时程序的执行
public void test() throws RuntimeException {
try {
testA(new A());
testB(new B());//处理错误
testC(new C());
} catch (Exception e) {
throw new MyRuntimeException();
} finally {
testD(new D());
}
}
- 假如上面程序都顺利执行,testB这里没有处理出错,则执行顺序是testA->testB->testC->testD
- 假如程序在testB方法中出现了处理错误,则testA方法是顺利执行的,testB方法则是执行失败,然后直接跳到catch里面了,不会执行testC方法,执行完catch的内容后,会执行finally的testD方法。
四、 常用的异常处理设计
-
异常枚举类
public enum ExceptionCode { SUCCESS(0, "操作成功!"), PARAMETER_INCORRECT(100001,"参数不正确"), UNAUTHENTICATED(100002, "未经认证,请先登录"), UNAHTUORIZED(100003, "未经授权,请联系客服"), DATABASE_OPERATION_EXCEPTION(200001, "数据库操作异常"), WECHAT_REDIRECT_EXCEPTION(900001, "微信重定向异常"), WECHAT_FETCH_ACCESS_TOKEN_EXCEPTION(900002, "微信获取 Access Token 异常"), SAB_USER_NOT_FOUND(700001, "用户不存在"), SAB_ROLE_NOT_FOUND(700002, "角色不存在"), SAB_DEPARTMENT_NOT_FOUND(700003, "部门不存在"), LOGIN_USER_NOT_FOUNT(700004, "用户不存在"), LOGIN_USER_PASSWORD_WRONG(700005, "用户密码错误"), LOGIN_USER_AUTHENTICATION(700006, "用户认证失败"), LOGIN_FAILED(700007, "登录失败"); private int code; private String msg; ExceptionCode(int code, String msg) { this.code = code; this.msg = msg; } public int getCode() { return code; } public String getMsg() { return msg; } }
-
基础异常类
public class BaseException extends RuntimeException { protected int code; protected String msg; public BaseException(ExceptionCode businessCode) { super(businessCode.getMsg()); this.code = businessCode.getCode(); this.msg = businessCode.getMsg(); } public BaseException(int code, String msg) { super(msg); this.code = code; this.msg = msg; } public int getCode() { return code; } public String getMsg() { return msg; } }
-
异常子类
public class SQLException extends BaseException { public SQLException () { super(ExceptionCode.DATABASE_OPERATION_EXCEPTION); } }
-
全局异常处理
@ControllerAdvice public class GlobalExceptionHandler extends BaseController { @ResponseStatus(HttpStatus.EXPECTATION_FAILED) @ResponseBody @ExceptionHandler(value = BaseException.class) public JSONObject handleBaseException(BaseException e) { return buildResponse(e.getCode(), e.getMsg(), null); } /* * SQLException是BaseException的子类,假如发生SQLException异常 * 根据异常匹配规则是优先捕捉到SQLException异常的,所以是进入这个异常处理方法 */ @ResponseStatus(HttpStatus.EXPECTATION_FAILED) @ResponseBody @ExceptionHandler(value = SQLException.class) public JSONObject handleSQLException(SQLException e) { return buildResponse(e.getCode(), e.getMsg(), null); } }
-
基础控制器类
public class BaseController { public JSONObject buildResponse() { return buildResponse(ExceptionCode.SUCCESS, null); } public JSONObject buildResponse(Object data) { return buildResponse(ExceptionCode.SUCCESS, data); } public JSONObject buildResponse(ExceptionCode businessCode, Object data) { return buildResponse(businessCode.getCode(), businessCode.getMsg(), data); } public JSONObject buildResponse(int code, String msg, Object data) { JSONObject response = new JSONObject(); response.put("code", code); response.put("msg", msg); response.put("data", data); response.put("time", System.currentTimeMillis()); return response; } }