Exception 和 Error 有什么区别

Exception 和 Error 有什么区别

十二月 23, 2019

Error

虚拟机生成并抛出,大多数错误与代码编写者所执行的操作无关。例如,Java虚拟机运行错误(Virtual
MachineError),当JVM不再有继续执行操作所需的内存资源时,将出现
OutOfMemoryError。这些异常发生时,Java虚拟机(JVM)一般会选择线程终止;还有发生在虚拟机试图执行应用时,如类定义错误(NoClassDefFoundError)、链接错误(LinkageError)。这些错误是不可查的,因为它们在应用程序的控制和处理能力之
外,而且绝大多数是程序运行时不允许出现的状况。对于设计合理的应用程序来说,即使确实发生了错误,本质上也不应该试图去处理它所引起的异常状况。在Java中,错误通常是使用Error的子类描述。

Exception

在Exception分支中有一个重要的子类RuntimeException(运行时异常),该类型的异常自动为你所编写的程序定义ArrayIndexOutOfBoundsException(数组下标越界)、NullPointerException(空指针异常)、ArithmeticException(算术异常)、MissingResourceException(丢失资源)、ClassNotFoundException(找不到类)等异常,这些异常是不检查异常,程序中可以选择捕获处理,也可以不处理。这些异常一般是由程序逻辑错误引起的,程序应该从逻辑角度尽可能避免这类异常的发生;而RuntimeException之外的异常我们统称为非运行时异常,类型上属于Exception类及其子类,从程序语法角度讲是必须进行处理的异常,如果不处理,程序就不能编译通过。如IOException、SQLException等以及用户自定义的Exception异常,一般情况下不自定义检查异常。
Exception又分为可检查(checked)异常和不检查(unchecked)异常

  1. 可检查异常在源代码里必须显式地进行捕获处理,这是编译期检查的一部分。前面我介绍的不可查的 Error,是 Throwable 不是 Exception。
  2. 不检查异常就是所谓的运行时异常,类似 NullPointerException、ArrayIndexOutOfBoundsException 之类,通常是可以编码避免的逻辑错误,具体根据需要来判断是否需要捕获,并不会在编译期强制要求。

异常处理良好规范

1. 尽量不要捕获类似 Exception 这样通用的异常,而是应该捕获特定异常

这是因为在日常的开发和合作中,我们读代码的机会往往超过写代码,软件工程是门协作艺术,所以我们有义务让自己的代码能够直观的体现出尽量多的信息,而泛泛的 Exception 之类,恰恰隐藏了我们的目的。另外,我们也要保证程序不会捕获到我们不希望捕获的异常。比如,你可能更希望 RuntimeException 被扩散出来,而不是被捕获。
进一步讲,除非深思熟虑了,否则不要捕获 Throwable 或者 Error,这样很难保证我们能够正确处理异常。

2. 不要生吞异常

如果我们不把异常抛出来,或者也没有输出到日志之类,程序可能在后续代码以不可控的方式结束。没人能够轻易判断究竟是哪里抛出了异常,以及是什么原因产生了异常。

3. try-catch 代码段会产生额外的性能开销

try-catch 代码段往往会影响 JVM 对代码进行优化,所以建议仅捕获有必要的代码段,尽量不要一个大的 try 包住整段的代码;与此同时,利用异常控制代码流程,也不是一个好主意,远比我们通常意义上的条件语句 (if / else, switch)要低效
Java 每实例化一个 Exception,都会对当时的栈进行快照,这是一个相对比较重的操作,如果发生的非常频繁,这个开销可就不能被忽略了。

4. 不要在 finally 代码块中处理返回值

按照我们程序员的惯性认知:当遇到 return 语句的时候,执行函数会立刻返回。但是,在 Java 语言中,如果存在 finally 就会有例外。除了 return 语句, try 代码块中的 break 或 continue 语句也可能使控制权进入 finally 代码块。
请勿在 try 代码块中调用 return, break, continue 语句。万一无法避免,一定要确保 finally 的存在不会改变函数的返回值。
函数的返回值有两种类型:值类型和对象引用,对于对象引用,要特别小心,如果在 finally 代码块中对函数返回的对象成员属性进行了修改,即使不在 finally 块中显示调用 return 语句,这个修改也会作用于返回值上。

5. 当一个 try 后跟了很多个 catch 时,必须先捕获小的异常再捕获大的异常。