典型问答

Q:请对比 Exception 和 Error,另外,运行时异常与一般异常有什么区别?

A:
Exception 和 Error 都继承 Throwable 类,该类是Java异常处理机制的基本组成类型,只有Throwable类型的示例才可以被抛出(throw)或捕获(catch)。
Exception 和 Error 体现了 Java 平台设计者对不同异常情况的分类。
Error 是指在正常情况下,不大可能出现的情况,绝大部分的 Error 都会导致程序(比如 JVM 自身)处于非正常的、不可恢复状态。既然是非正常情况,所以不便于也不需要捕获,常见的比如 OutOfMemoryError 之类,都是 Error 的子类。
Exception 是程序正常运行中,可以预料的意外情况,可能并且应该被捕获,进行相应处理。
Exception 又分为可检查(checked)异常和不检查(unchecked)异常,可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分。
不检查异常就是所谓的运行时异常(RuntimeException),类似 NullPointerException、ArrayIndexOutOfBoundsException 之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。

考点分析

  • 理解 Throwable、Exception、Error 的设计和分类
    【必须】掌握那些应用最为广泛的子类,以及如何自定义异常等。

Exception.png

  • 理解 Java 语言中操作 Throwable 的元素和实践
    【必须】掌握最基本的语法是,如 try-catch-finally 块,throw、throws 关键字等以及典型场景的处理。
意义 位置
throw 将产生的异常抛出(强调动作),抛出的既可以是异常引用,也可以是异常对象 方法体内
throws 如果一个方法可能会出现异常,但没有能力处理这种异常,可以在方法声明处用throws子句来声明抛出异常。用它修饰的方法向调用者表明该方法可能会抛出异常(可以是一种类型,也可以是多种类型,用逗号隔开) 方法名或方法名列表之后,方法体前

异常处理代码比较繁琐,比如我们需要写很多千篇一律的捕获代码,或者在 finally 里面做一些资源回收工作。随着 Java 语言的发展,JDK7以后引入了一些更加便利的特性,比如 try-with-resources(语法) 和 multiple catch(语法),具体可以参考下面的代码段。在编译时期,会自动生成相应的处理逻辑,比如,自动按照约定俗成 close 那些扩展了 AutoCloseable 或者 Closeable 的对象。

  1. try (BufferedReader br = new BufferedReader(…);
  2. BufferedWriter writer = new BufferedWriter(…)) {// Try-with-resources
  3. // do something
  4. catch ( IOException | XEception e) {// Multiple catch
  5. // Handle it
  6. }

Closeable.png

知识扩展

异常处理原则

  1. 尽量不要捕获类似 Exception 这样的通用异常,而是应该捕获特定异常。
  2. 不要生吞(swallow:假设这段代码可能不会发生异常,或者感觉忽略异常也无所谓)异常
    必须抛出异常或输出到日志。
  3. “throw early,catch late”原则。
    eg:“throw early”——NPE(NullPointerException)
    ```java public void readPreferences(String fileName){ //…perform operations… InputStream in = new FileInputStream(fileName); //…read the preferences file… }
  1. 如果fileNamenull,程序就会抛出NullPointerException,但由于没有第一时间暴露问题,堆栈信息可能令人费解,往往需要相对复杂的定位。修改后,让问题“throw early”,对应的异常信息就非常直观。
  2. ```java
  3. public void readPreferences(String filename) {
  4. /**
  5. * 检查指定的对象引用不是null,该方法主要用于在方法和构造函数中进行参数验证
  6. *
  7. * @param obj the object reference to check for nullity
  8. * @param <T> the type of the reference
  9. * @return {@code obj} if not {@code null}
  10. * @throws NullPointerException if {@code obj} is {@code null}
  11. */
  12. Objects. requireNonNull(filename);
  13. //...perform other operations...
  14. InputStream in = new FileInputStream(filename);
  15. //...read the preferences file...
  16. }

“catch late”处理原则:如果不知道如何处理,保留原有cause信息,直接抛出或构建新的异常抛出。在更高层面,有了清晰的(业务)逻辑,往往会更清楚合适的处理方式。