Exception和Error的比较&自定义全局异常处理

目录

1 理解 Throwable、Exception、Error 的设计和分类

2 异常处理机制

2.1 异常抛出 throws

2.2 异常捕获 catch

3 自定义全局异常处理(springBoot)


本文主要想解释两个问题:

1、对比Exception 和 Error

2、运行时异常和一般的异常有什么区别?

3、如何处理运行时异常?

回答1:Exception 和 Error 都是继承了 Throwable 类,Java 中只有 Throwable 类的实例才可以被抛出,或者被捕获。Error 顾名思义,是错误。它会导致程序处于非正常、不可恢复的状态。Exception 是程序中可以预料到的情况,可以被程序捕获并做相应的处理。

回答2:Exception 又分为可检查异常和不可检查异常。

非运行时异常是指通过编译可以发现的异常,比如IOException;Java编译器会检查它,也就是说,当程序中可能出现这类异常,要么用try-catch语句捕获它,要么用throws子句声明抛出它,否则编译不会通过。

运行时异常就是运行时异常,包括NullPointerException、ArrayIndexOutOfBoundsException等。编译阶段无法捕获的异常,在运行时由系统抛出。

出现运行时异常后,如果没有捕获处理这个异常(即没有catch),系统会把异常一直往上层抛,一直到最上层,如果是多线程就由Thread.run()抛出,如果是单线程就被main()抛出。抛出之后,如果是线程,这个线程也就退出了。如果是主程序抛出的异常,那么这整个程序也就退出了。也就是说,你如果不对运行时异常进行处理,那么出现运行时异常之后,要么是线程中止,要么是主程序终止。 
如果不想终止,则必须捕获所有的运行时异常,不让这个处理线程退出。 

 

1 理解 Throwable、Exception、Error 的设计和分类

          

常见的RuntimeException(运行时异常): 
IndexOutOfBoundsException(下标越界异常) 
NullPointerException(空指针异常) 
NumberFormatException (String转换为指定的数字类型异常) 
ArithmeticException -(算术运算异常 如除数为0) 
ArrayStoreException - (向数组中存放与声明类型不兼容对象异常) 
SecurityException -(安全异常) 
IOException(其他异常) 
FileNotFoundException(文件未找到异常。) 
IOException(操作输入流和输出流时可能出现的异常。) 
EOFException (文件已结束异常)
 

2 异常处理机制

捕捉并处理知道如何处理的异常,而抛出不知道如何处理的异常。当Java内置的异常都不能明确的说明异常情况的时候,需要创建自己的异常。

2.1 异常抛出 throws

通过Java的throw语句抛出异常。从方法中抛出的任何异常都必须使用throws子句。

throws Exception 有两种用法:

第一种是方法后面,表示:本方法不处理异常,交给调用方处理(如果你不希望异常层层往上抛,你就要用throws Exception) ,在调用该方法时,必须加上try...catch才可以(你加上throw exception。调用的地方就必须try catch,不然编译都不过。这样代码就更健壮了)。

如果没有加throws Exception的方法抛异常了,调用方也没有处理,异常就会一层一层抛出,最终交给JVM去处理的话,程序会中断,如果在程序中捕获的话,不会中断程序。

第二种是throw new Exception 表示人为的抛出一个异常。

2.2 异常捕获 catch

捕捉异常通过try-catch语句或者try-catch-finally语句实现。

try {  
	// 可能会发生异常的程序代码  
} catch (Type1 id1){  
	// 捕获并处置try抛出的异常类型Type1  
} catch (Type2 id2){  
	 //捕获并处置try抛出的异常类型Type2  
}

Java通过异常类描述异常类型,对于有多个catch子句的异常程序而言,应该尽量将捕获底层异常类的catch子句放在前面,同时尽量将捕获相对高层的异常类的catch子句放在后面。否则,捕获底层异常类的catch子句将可能会被屏蔽。
      例如:RuntimeException异常类包括运行时各种常见的异常,ArithmeticException类和ArrayIndexOutOfBoundsException类都是它的子类。因此,RuntimeException异常类的catch子句应该放在 最后面,否则可能会屏蔽其后的特定异常处理或引起编译错误。
 

总结: 异常要及早捕获、延迟处理。要尽量捕获具体类型的异常。

3 自定义全局异常处理

但是在实际项目中,我们都不可能在每个接口实现里面都try-catch一下。一般在业务中,都会自定义自己的运行时异常。因为笔者的项目是基于springBoot,所以在项目中对异常进行全局统一处理。

项目需求:1、前后端分离,只需要返回apiException; 2、不只是404或者500的异常;3、捕捉到异常之后需要做特殊化的处理。

方式:基于@ExceptionHandle  @ControllerAdvice注解的Controller层的全局异常统一处理。

在spring 3.2中,新增了@ControllerAdvice 注解,这个注解注释的类实现控制器增强的功能,在其中可以定义@ExceptionHandler、@InitBinder、@ModelAttribute,并应用到所有@RequestMapping注释的方法中。

1、首先自定义全局异常处理类:

/**
 * 系统通用的业务Exception,最终会转化成JsonResult形式
 *
 */
public class ApiException extends RuntimeException {   // 继承RuntimeException
    private int status;
    private String message;

    /**
     * 自定义业务异常,最终会转化成JsonResult形式
     * @param status 对应JsonResult.status
     * @param message 对应JsonResult.message
     */
    public ApiException(int status, String message) {
        this.status = status;
        this.message = message;
    }

    /**
     * 通过通用错误来构建异常,最终会转化成JsonResult形式
     * @param apiError 通用的错误
     */
    public ApiException(ApiError apiError) {
        this(apiError.value(), apiError.getMessage());
    }

    public ApiException(Throwable cause) {
        super(cause);
        this.status = ApiError.SERVER_ERROR.value();
        this.message = ApiError.SERVER_ERROR.getMessage();
    }

    public int getStatus() {
        return status;
    }

    @Override
    public String getMessage() {
        return message;
    }
}

2、@ExceptionHandle 实现自定义全局类处理

@ControllerAdvice
@Order(Ordered.LOWEST_PRECEDENCE)
public class AlarmExceptionHandler {
    private Logger logger = LoggerFactory.getLogger(AlarmExceptionHandler.class);

    @ResponseBody
    @ExceptionHandler
    public JsonResult processException(Exception ex) {

        if (null == ex) {
            logger.warn("拦截器抛出空异常");
        } else if (null != ex && (ex instanceof BindException)) {
            logger.warn(ex.getMessage(), ex);
        } else {
            logger.error(ex.getMessage(), ex);
        }

        JsonResult jsonResult;
        if (ex instanceof ApiException) {
            ApiException error = ((ApiException) ex);
            jsonResult = JsonResult.buildFailResult(error.getStatus(), "server error reason:" + error.getMessage(), null);

        } else if (ex instanceof BindException) {
            BindException c = (BindException) ex;
            List<FieldError> errors = c.getBindingResult().getFieldErrors();
            StringBuilder errorMsg = new StringBuilder();
            for (FieldError error : errors) {
                errorMsg.append(error.getField()).append(":").append(error.getDefaultMessage()).append(";");
            }
            jsonResult = JsonResult.buildFailResult(ApiError.PARAMS_ERROR.value(), errorMsg.toString(), null);
        } else {
            jsonResult = JsonResult.buildFailResult(ApiError.SERVER_ERROR.value(), "server error reason:" + ex.getMessage(), null);
        }

        return jsonResult;
    }
}

经过测试发现可以捕获controller层的异常,前提是controller 层不对异常进行处理。如果在service 中抛出异常,也不做处理的话,那么在这里也可以捕获的到。

因为不仅仅有404和500类型的错误,还需要自定义接口错误码。

public enum ApiError {

    // ---------------------------------------------
    // 通用错误码
    // ---------------------------------------------

    SERVER_ERROR(101, "Server error."),
    PARAMS_ERROR(102, "Parameter error: %s"),
    TICKET_ERROR(104, "Ticket error"),
    SIGN_ERROR(105, "Sign error"),
    ;

    ApiError(int code, String message) {
        this.code = code;
        this.message = message;
    }


    private final int code;
    private final String message;
    private Object[] parameters = null;

    public ApiError withParams(final Object... parameters) {
        this.parameters = parameters;
        return this;
    }

    /**
     * 返回枚举值
     * @return
     */
    public int value() {
        return this.code;
    }

    /**
     * 错误信息格式,如果有参数拼接参数
     * @return
     */
    public String getMessage() {
        return (null == parameters) ? message : String.format(message, parameters);
    }

    /**
     * 根据枚举值返回实例
     * @param value
     * @return
     */
    public static ApiError valueOf(int value) {
        if (value >= 1 && value <= ApiError.values().length) {
            return ApiError.values()[value - 1];
        } else {
            return null;
        }
    }


    @Override
    public String toString() {
        return "[" + code + "]: " + message;
    }

    public Object[] getParameters() {
        return parameters;
    }
}

上面的@ExceptionHandle 还可以拦截到 BindException 类型的错误,这种异常主要是由于 在使用注解 @Validator 时捕获的异常,可以参见另外一篇文章: @NotNull 和 @NotEmpty 和@NotBlank 区别

 

以上。?

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值