Exception 和 Error 都是 Throwable 类的子类(在Java代码中只有继承了 Throwable 类的实例才可以被 throw 或者被 catch)它们表示在程序运行时发生的异常或错误情况。
总结来看: Exception 表示可以被处理的程序异常,Error 表示系统级的不可恢复错误。
详细说明:
1)Exception :是程序中可处理的异常情况,表示程序逻辑或外部环境中的问题,可以通过代码进行恢复或处理。
常见的子类有:IOException 、SQLException 、NullPointerException 、IndexOutOfBoundsException 等。
Exception 又分为 Checked Exception (编译期异常)和 Unchecked Exception (运行时异常)。
- Checked Exception:在编译时必须显式处理(如使用 try-catch 块或者通过 rthrows 声明抛出)。如 IOException 。
- Unchecked Exception:运行时异常,不需要显式捕获。常见的如 NullPointerException 、IllegalArgumentException 等,继承自 RuntimeException 。
2)Error:表示严重的错误,通常是 JVM 层次内系统级的、无法预料的错误,程序无法通过代码进行处理或恢复。例如内存耗尽( OutOfMemoryError )、栈溢出( StackOverflowError )。
Error 不应该被程序捕获或处理,因为一般出现这种错误时程序无法继续运行。
扩展知识
异常处理时需要注意的六个点
1.尽量不要捕获类似 Exception 这样通用的异常,而应该捕获特定的异常。
软件工程是一门协作的艺术,在日常开发中我们有义务使自己的代码能更直观、清晰地表达出我们想要表达的信息。
但是如果你什么异常都用了 Exception ,那别的开发同事就不能一眼得知这段代码实际想要捕获的异常,并且这样的代码也会捕获到可能你希望它抛出而不希望捕获的异常。
2.不要“吞”了异常
如果我们捕获了异常,不把异常抛出,或者没有写到日志里,那会出现什么情况?线上除了 bug 莫名其妙的没有任何的信息,你都不知道哪里出错以及出错的原因。
这可能会让一个简单的 bug 变得难以诊断,而且有些同学比较喜欢用 catch 之后用 e.printSrackTrace(),在我们产品中通常不推荐用这种方法,一般情况下这样是没有问题的但是这个方法输出的是个标准错误流。
比如在分布式系统中,发生异常但是找不到 stacktrace 。
所以最好是输出到日志里,我们可以自定义一定的格式,将详细的信息输入到日志系统中,适合清晰高效的排查错误。
3.不要延迟处理异常
比如你有个方法,参数是个 name ,函数内部调用了别的好几个方法,其实你的 name 传的是 null 值,但是你没有在进入这个方法或者这个方法一开始就处理这个情况,而是在你调用了别的好几个方法然后爆出这个空指针。
这样的话明明你的出错堆栈信息只需要抛出一点点信息就能定位到这个错误所在的地方,经过了许多方法之后可能就是一坨堆栈信息。
4.只在需要 try-catch 的地方 try-catch ,try-catch 的范围能小则小
只在必要的代码段使用 try-catch ,不要不分青红皂白 try 住一坨代码,因为 try-catch 中的代码会影响 JVM 对代码的优化,例如重排序。
5.不要通过异常来控制程序流程
一些可以用 if/else 的条件语句来判断例如 null 值等,就不要用异常,异常肯定是比一些条件语句低效的,有 CPU 分支预测的优化等。
而且每实例化一个 Exception 都会对栈进行快照,相对而言这是一个比较重的操作,如果数量过多开销就不能被忽略了。
6.不要在 finally 代码块中处理返回值或者直接 return
在 finally 中 return 或者处理返回值会发生很诡异的事情,比如覆盖了 try 中的 return ,或者屏蔽的异常。