讀本篇文章之前,如果讓你敘述一下 Exception Error Throwable 的區別,你能回答出來么?
你的反應是不是像下面一樣呢?
- 你在寫代碼時會經常 try catch(Exception)
- 在 log 中會看到 OutOfMemoryError
- Throwable 似乎不常見,但也大概聽說過
一、Exception Error Throwable 關系
直接看下圖,展示了三者之間的關系:
Throwable是Error和Exception的父類,Exception是程式正常運行中可預料的正常情況,應該被捕獲并進行處理,- 又分為可檢查(checked)和不檢查(unchecked)例外,
- 可檢查例外是在編譯期檢查的一部分,必須顯示捕獲處理,如有的方法 throw exception,那么呼叫該函式則必須 catch 處理或者再次 throw 出去交給下一層處理,
- 不檢查例外一般指運行時例外(RuntimeException),類似
ArrayIndexOutOfBoundsException、ArithmeticException等,一般可由代碼邏輯避免,可看情況是否捕獲,
Error一般是正常情況下不太可能出現的,絕大部分 Error 會導致程式處于不可恢復的狀態,所以也不必捕獲,如OutOfMemoryError,

二、對比一個 Error 和 Exception
你在面試中也許會被問到:
NoClassDefFoundError 和 ClassNotFoundException 有什么區別?
首先,我們看這倆名字,一個是 Error 另一個是 Exception,從上面的介紹以及看下面的繼承圖可以得到:ClassNotFoundException 應是編碼時要被捕獲的例外,NoClassDefFoundError 是編譯通過了,但運行時產生的重大問題,

進一步的:
ClassNotFoundException 是運行中動態加載類時出現的問題,
舉例來說,使用 Class.forName 來動態加載一個類,如果你不顯示的 catch 處理,ide 都會給你提示,并且也過不了編譯,
// 錯誤寫法
public void except() {
Class.forName("com.test.aaa");
}
// 正確寫法
public void except() {
try {
Class.forName("com.test.aaa");
} catch (ClassNotFoundException e) {
// throw or log
throw new RuntimeException(e);
// log.error("ClassNotFoundException: ", e);
}
}

NoClassDefFoundError 是編譯時沒問題,但運行時 new 實體找不到,
比如在一個類中參考另一個類的函式,編譯后把另一個類的 class 檔案刪掉:
public class Main {
public static void main(String[] args) {
System.out.println("Hello world!");
MyPrint.printName();
}
}
public class MyPrint {
public static void printName() {
System.out.println("my name is zhangsan");
}
}
使用 javac 編譯,再洗掉 MyPrint.class
$ tree com
com
└── shuofxz
├── Main.class
├── Main.java
├── MyPrint.class # 把這個檔案刪掉
└── MyPrint.java
執行程式,就會看到 NoClassDefFoundError,并且是由 ClassNotFoundException 引起的,
Exception in thread "main" java.lang.NoClassDefFoundError: com/shuofxz/MyPrint
at com.shuofxz.Main.main(Main.java:6)
Caused by: java.lang.ClassNotFoundException: com.shuofxz.MyPrint
at java.net.URLClassLoader.findClass(URLClassLoader.java:387)
at java.lang.ClassLoader.loadClass(ClassLoader.java:419)
at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:352)
at java.lang.ClassLoader.loadClass(ClassLoader.java:352)
... 1 more
三、捕獲例外代碼常見問題
一)看下面的代碼有什么問題?
try {
// 業務代碼
// ...
Thread.sleep(1000L);
} catch (Exception e) {
}
- 捕獲了過于通用的例外
Exception,應改為對應的InterruptedException,這么做的目的是因為:第一方便閱讀代碼,知道可能會出現什么具體的例外;第二不捕獲意料之外的例外, - 不要捕獲例外之后啥都不做(生吞例外),這就是給自己挖坑,之后程式遇到問題,你很難定位到這里,
二)再看下面這個,增加了例外列印邏輯,還有什么問題不?
try {
// 業務代碼
// …
} catch (IOException e) {
e.printStackTrace();
}
- 自娛自樂是 ok 的,但不要放到生產環境中,因為
e.printStackTrace()的功能是:Prints this throwable and its backtrace to the standard error stream,你很難判斷它到底輸出到哪里去了, - 應該用成熟的日志工具如 Slf4j 等,
三)再來看下面的:
try {
// 業務邏輯 A
// 業務邏輯 B
// 業務邏輯 C
// ...
} catch (Exception e) {
log.error("have exception", e);
}
- 不能因為怕丟失例外捕獲,就把一大段代碼都框到一個 try-catch 模塊中,
- try-catch 代碼段會產生額外的性能開銷,它往往會影響 JVM 對代碼進行優化,
四)我們前面介紹了Exception、Error、Throwable,為什么代碼中經常能看到 catch XXException,卻幾乎看不到 catch XXError 或 catch Throwable 呢?
- Exception 才是你應該關注處理的例外,這種例外處理后還可以使程式正常運行,
- Error 屬于重大問題,是會使程式直接崩潰的,你捕獲了也沒什么用,很難讓程式再「活」過來,
- 至于 Throwable,首先不應該不過這么寬泛的問題(比捕獲 Exception 還嚴重),第二其中包含了 Error 也不是你應該處理的問題,
- 因此,Error 和 Throwable 除非你明確知道你在干什么,否則不要捕獲這兩種,
三、小結
本篇介紹了 Exception Error Throwable 的區別,并給出了相關例子幫助理解,
回到開頭的問題:「敘述一下 Exception Error Throwable 的區別」你心里有數了么?
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/539095.html
標籤:Java
