今天在看Zookeeper
原始碼的時候發現在其原始碼中使用到了UncaughtExceptionHandler
處理非檢查異常,關鍵是剛開始看的時候我不會呀!所以得學習呀!所以便出現了下文。
一、Java的異常型別
這是一張Java異常體系大致圖:
從大體上說,Java中的異常分為兩種:
-
1、非檢查異常
非檢查異常為
Error
和RuntimeException
及其子類,javac
在編譯時,不會提示和發現這樣的異常,不要求在程式處理這些異常。所以如果願意,我們可以編寫程式碼處理(使用try…catch…finally
)這樣的異常,也可以不處理。對於這些異常,我們應該修正程式碼,而不是去通過異常處理器處理 。這樣的異常發生的原因多半是程式碼寫的有問題。如除0
錯誤ArithmeticException
,錯誤的強制型別轉換錯誤ClassCastException
,陣列索引越界ArrayIndexOutOfBoundsException
,使用了空物件NullPointerException
等等 -
2、檢查異常
檢查異常則是除了
Error
和RuntimeException
的其它異常。javac強制要求程式設計師為這樣的異常做預備處理工作(使用try…catch…finally
或者throws
)。在方法中要麼用try-catch語句捕獲它並處理,要麼用throws
子句宣告丟擲它,否則編譯不會通過。這樣的異常一般是由程式的執行環境導致的。因為程式可能被執行在各種未知的環境下,而程式設計師無法干預使用者如何使用他編寫的程式,於是程式設計師就應該為這樣的異常時刻準備著。如SQLException
,IOException
,ClassNotFoundException
等。
總結下:
- 1、檢查異常,非檢查異常都可以使用
try…catch…finally
塊捕獲異常 - 2、非檢查異常屬於執行時異常,編譯器不會提示你進行捕獲
為什麼我要進行總結呢?下面我將說到!
二、UncaughtExceptionHandler的使用
很多時候我們寫完程式碼,執行之後老是會報空指標異常,我們卻沒發現應該去捕獲它。特別是在多執行緒環境中,執行緒類Thread
的run()
方法是沒有丟擲任何異常資訊的,如果在run()
方法中報一個空指標異常,我們卻沒有捕獲它,那後果將會是使執行緒中斷,執行緒中斷可能並不是我們想看到的結果。
最可氣的是,子執行緒丟擲的異常資訊,在其主執行緒同樣是捕獲不到的。下面舉例子演示下:
public class NoCaughtThread {
public static void main(String[] args) {
// 捕獲不到
try {
Thread thread = new Thread(new Task());
thread.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
class Task implements Runnable {
@Override
public void run() {
throw new NullPointerException();
}
}
複製程式碼
程式執行結果:
Exception in thread "Thread-0" java.lang.NullPointerException
at org.apache.zookeeper.server.quorum.Task.run(NoCaughtThread.java:21)
at java.lang.Thread.run(Unknown Source)
複製程式碼
為了處理這些未捕獲的異常,jdk自帶了UncaughtExceptionHandler
類進行處理,例項如下:
import java.lang.Thread.UncaughtExceptionHandler;
public class NoCaughtThread {
public static void main(String[] args) {
// 設定未捕獲異常處理器,這裡是預設的,當然你可以自己新建一個類,然後實現UncaughtExceptionHandler介面即可
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.err.println("程式丟擲了一個異常,異常型別為 : " + e);
}
});
Thread thread = new Thread(new Task());
thread.start();
}
}
class Task implements Runnable {
@Override
public void run() {
throw new NullPointerException();
}
}
複製程式碼
程式輸出:
程式丟擲了一個異常,異常型別為 : java.lang.NullPointerException
複製程式碼
三、總結
總結一下,歸根到底,我覺得之所以需要使用到UncaughtExceptionHandler
,很多情況下都是因為我們自己程式碼寫的不好,不夠嚴謹,或者說我們在寫程式碼的時候沒有考慮周到。比如最常見的就是丟擲空指標NullPointerException
,請看下面程式碼:
Map<String, String> map = null;
System.err.println(map.get("name"));
複製程式碼
上面的程式碼必定丟擲空指標異常,你可能會說,哇,誰這麼笨,這麼明顯宣告map=null
,好,為了使你信服,我決定打個響指:
// 從資料庫查詢一個使用者
User user = userDao.getUser(12);
System.err.println(user.getUserName("name"));
複製程式碼
可以看出,如果user
為null
,則必定也是丟擲空指標異常。很多很多非檢查異常我們都需要儘量避免,在編寫程式碼的時候多考慮是否會出現空指標,是否會出現陣列越界等等情況。