异常简介
异常是为了在出错的时候,提供一个机制把控制权交给能够处理这个错误的代码手上。如果没有异常,异常产生处和处理处的代码可能很远。
异常的层次结构:
基类:Throwable 直接继承Throwable:Error和Exception Error被设计成虚拟机内部错误,程序员不应该抛出 Exception是我们平常关注的范围。
IOException和RuntimeException是Exception下的两个常见的直接子类。Exception还有更多直接子类。
所有派生于Error类或RuntimeException类的异常都是非检查型unchecked异常。 所有其他异常都是检查型checked异常。
语义上,RuntimeException指的是编程错误导致的异常,如
- ArrayIndexOutOfBoundsException
- NullPointerException
- ClassCastException
创建异常类
我们需要继承一个Exception的子类。同时惯例我们应该实现两个构造器:一个无参和一个带详细信息字符串的构造器。
异常的捕获和传播
要捕获异常,需要建立一个try-catch块。 如果在try块中遇到catch指定的异常,那么直接跳到对应catch语句块。 预估遇到catch未指定的异常,那么方法直接退出。 如果try块正常运行,那么不运行catch块,继续运行方法的后续代码。
如何判断处理还是传递异常? 在工程实践中,推荐遵循**尽早抛出,延迟捕获(Throw early, catch late)**的原则: 在代码最底层的逻辑验证阶段,一旦检测到异常状态立即抛出;在整个调用链路中,除非能够真正处理问题或必须转译异常,否则应一路放行,交由拥有全局业务视野的顶层框架或统一异常处理器(如 Spring 的 @ExceptionHandler)进行集中处理。
finnally和try-with-resources
finally子句:无论是否排除是否被捕获的异常,都会在最后执行。 可以只有finally没有catch
不要在finally中使用throw, return, break, continue等改变控制流的语句。
一个良好的实践是使用两个独立的try,try-finally用于关闭资源,try-catch用于处理异常。
java7以上支持try-with-resources
对于实现了AutoClosable接口:
interface AutoClosable {
void close() throws Exception
}
的类(资源),try-with-resources语法会在try块结束后自动关闭这些资源。
try (Resource res = ...) {
do with res.....
} // 不管有没有异常,是否被捕获,都会在这里调用res.close()
catch (Exception e) {
//...
}
java9之后,可以提供effectively final的变量了:
// out 是方法的参数,满足effectively final
try (out) {
// do sth
} // out.close
没什么人在意的细节:在try-with-resources中,如果try块和close都抛出了异常,close抛出的异常会被抑制。它们被用addSuppressed方法加到try抛的异常上,然后重新抛出try抛出的异常。如果你感兴趣,可以使用getSuppressed方法获取这些异常。