作为一个稀有的Java妹子,所写的所有博客都只是当作自己的笔记,留下证据自己之前是有用心学习的~哈哈哈哈(如果有不对的地方,也请大家指出,不要悄悄咪咪的不告诉我)
Throwable,Exception,Error关系
Java中的异常机制可以便于开发人员定位程序中的错误,那么如何优雅的抛出异常呢?首先要清楚Java中的异常类,以及它们之间的关系。
异常主要分为两大类:Exception和Error,两个都继承Throwable类
public class Throwable implements Serializable
public class Error extends Throwable
public class Exception extends Throwable
Exception
Exception类是开发中经常会使用到的类,分为编译时异常和运行时异常。
编译时异常指:程序员在撸代码时,就要处理的异常,编译软件会以红色波浪线提示此处的代码需要手动抛出异常,不然编译不会通过;
File file = new File("/User/DownLoad/1.txt");
//这里就会爆红,只是我复制出来,不在idea中,看不到红色波浪线
FileInputStream io = new FileInputStream(file);
常见编译时异常 | 抛出原因 |
---|---|
FileNotFoundException | 文件有可能不存在,就会抛出此异常 |
ClassNotFoundException | 找不到具体指定类的定义 |
运行时异常指:在运行时才能发现的异常,编译期无法判断代码是否会有异常,也就是大家耳熟能详的bug,基本上都是运行时异常,因为编译期的异常只要不是红绿色盲基本上都能够try catch或是throw掉。
常见运行时异常 | 抛出原因 |
---|---|
NullPointerException | 空指针 |
ArrayIndexOutOfBoundsException | 数组下标越界 |
Error
Error一般是系统级别的,由Java虚拟机生成并抛出,程序内部无法捕获。常见的Error主要由程序进入死循环,内存泄露,栈溢出等等,这类异常只能通过分析代码,找出原因并优化代码。
如何捕获异常
1.try catch finally
当知道哪段代码会抛异常,或者有编译时异常时,通常我们会使用try catch finally来处理异常,这三个关键字不能单独出现,可以try…catch,try…finally,try…catch…finally,当然如果有多个异常的话,catch可以有多个(注意catch的顺序应该是子类在前,父类在后,因为当catch块有多个时,异常匹配到其中一个程序就停止了)。
finally是不管有没有抛异常都会执行的代码块,一般会把释放连接、释放锁等操作放在该代码块中,不建议在finally中使用return,这样就覆盖try和catch中的return,除非你想返回的结果就是finally中的。
try{
int i= 1/0;
}catch (ArithmeticException e1){
System.out.println("除数不能是0");
}
catch (Exception e){
System.out.println(e);
}finally {
System.out.println("finally");
}
结果:
除数不能是0
finally
2. throw
如果在当前方法中我们并不想处理这个异常,那么就把异常抛出,让调用方法者处理异常,在mvc中,往上抛到controller层就不能继续抛出了,需要使用try…catch捕获异常,封装异常信息返回前端。
/*
编译时异常通过在方法后面throws关键字,将异常往上抛出,
调用该方法的处理异常
*/
public static void test() throws FileNotFoundException {
File file = new File("/User/Download/1.txt");
FileInputStream io = new FileInputStream(file);
}
public static void main(String[] args) {
try {
/*因为test会抛出异常,那么就需要处理,从这里也可以推测
编译时异常方法,就是在方法中把异常抛出来,我们在使用
的时候就必须处理,要么继续往上抛,要么try...catch*/
test();
} catch (FileNotFoundException e) {
System.out.println("文件不存在!!");
}
}
/*字节流的源码,可以看到该方法会抛出异常,所以在调用该方法时我们需要手动
处理异常
*/
public FileInputStream(File file) throws FileNotFoundException {
String name = (file != null ? file.getPath() : null);
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(name);
}
if (name == null) {
throw new NullPointerException();
}
if (file.isInvalid()) {
throw new FileNotFoundException("Invalid file path");
}
fd = new FileDescriptor();
fd.attach(this);
path = name;
open(name);
}
3.自定义异常
有些业务层面的异常,比如用户信息不存在,该用户身份不能进行此操作等等。这类信息其实不算严格意义上的异常,只是不满足业务要求,像这类信息我们可以通过自定义异常来处理。
public class MyException extends Exception {
public MyException() {
super();
}
public MyException(String message){
super(message);
}
public MyException(String message, Throwable cause) {
super(message, cause);
}
public MyException(Throwable cause) {
super(cause);
}
protected MyException(String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
//一般将异常信息定义为枚举
public enum MyExceptionEnum {
USER_NOT_EXIST(201,"用户不存在");
private Integer code;
private String msg;
MyExceptionEnum(Integer code,String msg){
this.code = code;
this.msg = msg;
}
public Integer getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
public static void test() throws MyException {
UserDO userDO = userMapper.selectByPrimaryKey(1);
if(userDO ==null){
/*当用户不存在时,就抛出自定义异常,然后在controller层
捕获,返回错误信息给前端
*/
throw new MyException(MyExceptionEnum.USER_NOT_EXIST.getMsg());
}
}
4.Springmvc全局异常
Spring有一个全局异常处理器ExceptionHandler,只要在controller层将异常声明为全局异常,在service层抛出的全局异常,不需要在controller层显式的使用try…catch捕获,springmvc会自动封装model返回给前端。
需要注意的是自定义异常需要继承的是RuntimeException
public class MyException extends RuntimeException {
public MyException() {
super();
}
public MyException(String message){
super(message);
}
public MyException(String message, Throwable cause) {
super(message, cause);
}
public MyException(Throwable cause) {
super(cause);
}
protected MyException(String message, Throwable cause,
boolean enableSuppression,
boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
}
枚举类的定义不变,抛出的方式也不变,不过需要新增一个类定义全局异常
@RestControllerAdvice(annotations = RestController.class)
@Slf4j
public class ControllerExceptionHandleAdvice {
@ExceptionHandler(MyException.class)
public ResultVO myExceptionHandler(HttpServletResponse response,
MyException e) {
log.error("dubbo业务处理异常 {}", e.getMessage(), e);
response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
return ResultVO.fail(e.getMessage());
}
}
这样定义以后,controller就不要捕获异常,在哪里抛出就直接返回给前端