实习了这么久,现在才发现自己对异常抛出还是只是浅显略懂,这次趁这个机会复习一下java的异常捕获与抛出机制。
1.java捕获异常
简而言之就是try/catch机制,在try部分的代码发生错误时,将错误通过catch捕获,值得注意的是,catch可以有多个,并在每个代码块执行不同的方法。但是匹配机制为从上到下,且只会匹配一个,如果有上面的exception的子集在下面,那下面那个子集的catch将永远不会被匹配到,其会被上面的父类所全部捕获。具体可以参照这里
另外还有throws (在类上声明的部分)可以在方法可能出现问题时抛出异常,然后由调用者进行处理。
如先使用throws声明一个有可能出现错误的方法
然后调用时用try包含此方法
catch抛出同类的异常
注意:此处throws 的异常继承自Exception.class的话,在整个程序某地方必须进行try/catch捕获或者是再次抛出,因为Exception.class属于checked 异常,总要被一个地方所处理。而继承自runtimeException发生也不会导致整个程序停止,所以spring项目中抛出继承自runtimeException的也不用强制throws掉来保证程序的运行,程序会一步步向上抛到jvm给你默认处理(打印错误信息,停止当前线程)
如以下示例:
比如如果service层的一个方法声明了 throws Exception(checked异常),之后在controller层进行调用的话,就会显示Unhandled exception:。
编译器严格地执行 throws 说明符。 如果调用了一个抛出已检查异常的方法, 就必须对它进行处理, 或者将它继续进行传递
将要被调用的方法发现自己很有可能出现异常(当然也可以自己处理),其将异常向上抛出,当调用者进行调用方法时就会出现Unhandled exception:错误,只能在这里进行处理(try/catch)或者是继续抛出(try/catch的catch代码块中再次throw这个异常,如果其异常是exception或者是继承自exception异常的话,则需要再次声明throws xxxexception)
2.java抛出异常(自带异常)
(此处指抛出java自带的异常,实际上自定义异常也得抛)
void process2(String s) {
if (s==null) {
throw new NullPointerException();
}
}
public class Main {
public static void main(String[] args) {
try {
Integer.parseInt("abc");
} catch (Exception e) {
System.out.println("catched");
throw new RuntimeException(e);
} finally {
System.out.println("finally");
}
}
}
此方法可抛出其空指针异常
3.自定义异常(抛出)
自定义异常,说白了就是继承自官方异常根据具体业务增加部分业务异常,其有两个核心部分,一是自定义异常exception(继承自runtimeException/IOexception等异常),二是exceptionHandler(不过并不建议使用exceptionHandler直接命名,会和注解冲突。此handler类会在exception执行完毕后被指定执行)
如果需要自定义异常(比如业务中遇到的一些java中没有定义的异常),我们可以继承自java的异常类,进行实现自己业务类型的异常。
Exception
public class JsonException extends BaseException {
public JsonException(Status status) {
super(status);
}
public JsonException(Integer code, String message) {
super(code, message);
}
}
public class PageException extends BaseException {
public PageException(Status status) {
super(status);
}
public PageException(Integer code, String message) {
super(code, message);
}
}
@Data
@EqualsAndHashCode(callSuper = true)
public class BaseException extends RuntimeException {
private Integer code;
private String message;
public BaseException(Status status) {
super(status.getMessage());
this.code = status.getCode();
this.message = status.getMessage();
}
public BaseException(Integer code, String message) {
super(message);
this.code = code;
this.message = message;
}
}
在大型业务开发时,建议先定义一个baseException写下基础的方法,然后根据业务进行继承派生。
此处jsonException是前后端交互式用json传输报错数据,pageException指前后端不分离使用模板的情况下传输报错数据。
exceptionHandler
@ControllerAdvice
@Slf4j
public class DemoExceptionHandler {
private static final String DEFAULT_ERROR_VIEW = "error";
/**
* 统一 json 异常处理
*
* @param exception JsonException
* @return 统一返回 json 格式
*/
@ExceptionHandler(value = JsonException.class)
@ResponseBody
public ApiResponse jsonErrorHandler(JsonException exception) {
log.error("【JsonException】:{}", exception.getMessage());
return ApiResponse.ofException(exception);
}
/**
* 统一 页面 异常处理
*
* @param exception PageException
* @return 统一跳转到异常页面
*/
@ExceptionHandler(value = PageException.class)
public ModelAndView pageErrorHandler(PageException exception) {
log.error("【DemoPageException】:{}", exception.getMessage());
ModelAndView view = new ModelAndView();
view.addObject("message", exception.getMessage());
view.setViewName(DEFAULT_ERROR_VIEW);
return view;
}
}
handler类在开发的过程中需要注意的是:一,在类上注明@ControllerAdvice表明这个是增强类(advice(AOP相关)),二,在方法上注明@ExceptionHandler(value = ***.class)此处制定了其哪个exception类执行完毕后会由这个方法进行处理。
比如以上代码虽然实际走的是json/page的父类baseException的代码,继承后相当于json/pageException包括了其代码,所以其还是会被handler所引到对应的handler方法中。所以,其从baseException继承出的子目的则是将对应的exception引导向对应的handler进行处理