内容提要
本文总结Java异常处理体系:Throwable分为Error和Exception,Exception又分为检查异常与非检查异常。结合代码示例介绍NPE、算术异常、未声明异常、套接字异常等典型场景,并说明Spring通过@RestControllerAdvice统一处理异常,建议梳理异常知识地图以体系化学习。
延伸解读
异常分类的实用区分法
文章指出,检查异常与非检查异常可通过编译器行为快速区分:需要显式try-catch或throws声明的就是检查异常,否则为非检查异常。Error和RuntimeException及其子类属于非检查异常,其他Exception子类为检查异常。这种区分有助于在编码时判断哪些异常必须处理,哪些可以交由统一机制兜底。
Error与Exception的处理边界
根据文章,Error通常表示JVM层面出现问题,不应捕获而应抛出,一旦发生往往导致程序停止;Exception则意味着环境正常,需要开发人员处理。这一边界提醒开发者不要盲目捕获Error,而应将异常处理重点放在Exception体系上,避免掩盖严重系统问题。
Spring统一异常处理的注意点
文章通过@RestControllerAdvice和@ExceptionHandler(Throwable.class)示例展示了统一异常处理。作者建议使用Throwable而非仅Exception,以覆盖更广的异常情况。同时提到/error路径被Spring默认占用,自定义错误页面时需避开,否则可能返回白页或意外状态码。
动态代理异常的特殊性
文章说明,使用动态代理时,若被代理类抛出未声明的检查异常,代理类会抛出UndeclaredThrowableException,其根本原因可能被包装为InvocationTargetException。作者认为这是Java动态代理不优雅的处理方式,建议在阅读或模仿源码时注意这一点,避免在业务代码中依赖此类行为。
Q&A
Java异常体系中Throwable、Error和Exception的关系是什么?
Java所有异常的父类是Throwable,它分为Error和Exception。Error表示JVM层面出现的问题,正常情况下不应发生;Exception表示环境没有问题,需要开发人员处理。Exception又分为RuntimeException(运行时异常)和非运行时异常。
Java中检查异常和非检查异常如何区分?
检查异常和非检查异常的区分:Error、RuntimeException及其子类是非检查异常,其他是检查异常。写代码时,编译器提示需要try catch或throws的就是检查异常,其他是非检查异常。
Spring中如何统一处理异常?
Spring可以通过@RestControllerAdvice和@ExceptionHandler注解实现统一异常处理。例如定义一个类,使用@RestControllerAdvice标注,并在方法上使用@ExceptionHandler(Throwable.class)来捕获所有异常,返回统一格式的错误信息。
使用BigDecimal做除法时如何避免ArithmeticException?
使用BigDecimal.divide时,应使用三个参数的方法:divide(BigDecimal divisor, int scale, RoundingMode roundingMode),避免使用单参数方法。因为单参数方法在除不尽时会抛出ArithmeticException: Non-terminating decimal expansion。三参数方法会按照指定的舍入模式和保留位数处理。
Java动态代理中未声明异常是如何产生的?
使用动态代理时,如果被代理的类抛出了一个异常但没有throws声明,代理类找不到匹配的异常类型,会抛出InvocationTargetException,最终被包装为UndeclaredThrowableException。这是Java动态代理不优雅的处理方式。
Spring MVC默认如何处理404和500错误?
Spring MVC默认对不能处理的异常如404、500会返回白页。这是因为DispatcherServlet的doDispatch方法在找不到请求处理器时,会设置response状态码为404,并最终返回/error页面。