04 异常处理


异常

异常分为 Exception 和 Error,其继承关系如下

TODO: Insert inheritance image

Exception 是可以被处理的异常,如NullPointerExceptionIllegalArgumentException;Error 是不一定能被处理的异常,如OutOfMemoryErrorNoClassDefFoundError

如果 Exception 没有被处理,那么程序无法被编译。

方法可以通过throw关键词表示调用方法时可能抛出的异常,如String.getBytes(String)的签名

public byte[] getBytes(String charsetName) throws UnsupportedEncodingException {
    ...
}

在调用String.getBytes(String)时,必须使用一个 try-catch 块处理UnsupportedEncodingException

不过如果在另一个被标记为throws UnsupportedEncodingException的方法里调用String.getBytes(String),我们就不需要处理异常。异常处理会被下放到调用该方法的其他方法。最坏情况我们必须要在main方法里处理该异常,除非main也被标记为throws UnsupportedEncodingException。如

在处理异常时,我们可以使用printStackTrace()打印异常栈,如

try {
  // ...
} catch (UnsupportedEncodingException e) {
  e.printStackTrace();
}

try-catch 语句

多个 catch 语句按顺序分别处理不同异常

使用catch (ExceptionA | ExceptionB)同时处理多个异常

使用 finally 语句,无论异常是否发生,都在最后执行特定逻辑,如清理资源

抛出异常

使用throw关键词抛出异常

自定义异常

在大型项目中,我们会定义一套异常继承体系。

首先自定义一个BaseException,然后继承这一根异常,定义其他异常。

BaseException本身通常继承自RuntimeException

NullPointerException (NPE)

为避免 NPE,可以

  • 使用空数据,而非null。例如,返回一个空数组,或空字符串

  • 使用Optional<T>

断言

assert

日志(log)

Java.util.logging

Common Logging

Log4j

SLF4J 和 Logback

杂项

  • 如果 catch 和 finally 都抛出了异常,只有 finally 的异常会被处理,catch 的异常会被屏蔽(Suppressed Exception)。不推荐这么做。