知识导航

java中exception和error有什么区别_软件工程


异常会伴随着我们开发程序的整个周期,也只有正确处理好这些异常,才能保证程序的可靠性和稳定性。

各个主流编程语言都提供了很完善的异常处理机制,Java 也不例外,今天就梳理一下 Java 的异常

Java 类库的异常设计

java中exception和error有什么区别_Java_02

Exception 和 Error 都是继承了 Throwable 类。

throw 和 catch 只能作用在 Throwable 类的实例上。比如:

throw new RuntimeException("Error !");

Exception 和 Error 提现了 Java 设计者对于异常的不同分类。
Exception 是在程序运行过程中,可以预料到的正常情况,应该捕获这些异常,并作出相应的处理。

Exception 又可以分为可检查(Checked)和不可检查(Unchecked)异常。可检查异常是在源代码里必须进行显示的处理,这是编译器检查的一部分;不可检查异常就是运行时异常,如 NullPointerException,ArrayIndexOutOfBoundsException,通常是正确编码就可以解决的异常,可以按需求进行补货。

Error 表示在正常情况下,不大可能出现的情况,会导致程序处于非正常、不可恢复的状态。不便于也不需要捕获,如 OutOfMemoryError,StackOverflowError

尽量不要捕获 Exception 异常

不要去捕获 Exception 异常,应该去捕获特定异常。应为在日常的开发和团队合作中,读代码的机会会很多,软件工程是门代码合作的艺术,我们有义务让自己的代码能体现出更多的信息,而捕获 Exception 异常恰恰隐藏了我们的意图。

另外也要保证不要捕获到不应该捕获的异常,比如 RuntimeException,你可能更希望这个异常能抛出去

不要生吞异常

这是非常需要注意的事,比如下面的代码:

try {
   // 业务代码
   // …
} catch (IOException e) {
    e.printStackTrace();
}

printStackTrace 在 JDK 文档中第一句话就是:
Prints this throwable and its backtrace to the standard error stream。

在稍微复杂一点的生产系统中,标准输出不是个合适的选项,一般我们自己也不知道到底输出到哪去了,导致无法找到堆栈轨迹。没有人能够轻易判断到底是哪里出现了异常,以及是什么原因出现了异常。
生吞异常,本质上是不知道该如何处理,掩盖了问题。

最好是使用 第三方日志系统,输出到日志文件中

Throw early, catch late

来体会下面的一段代码:

public void readFile(String filename) {
  Objects. requireNonNull(filename);
  //...perform other operations... 
  InputStream in = new FileInputStream(filename);
   //...read the preferences file...
}

如果文件名是 null 的话,会抛出空指针异常。所以这里尽早的发现了这个问题,并且抛出了合理的异常出去。实际情况中,可能是配置文件没找到,配置文件格式不对,需要尽早的发现此类问题,尽早的抛出去。

抛出去之后,到底应该怎么捕获呢?可以直接打印日志出去,或者保留原有的 cause,构造新的异常抛出去,在上层逻辑(业务逻辑)中就知道应该做何种处理了

合理的捕获和抛出异常,也是一门很大的学问。
当我们的服务性能下降、吞吐量下降的时候,去日志找发生最频繁的异常也是很好的思路。