Java捕獲非檢查異常----UncaughtExceptionHandler的使用

擁抱心中的夢想發表於2018-05-16

今天在看Zookeeper原始碼的時候發現在其原始碼中使用到了UncaughtExceptionHandler處理非檢查異常,關鍵是剛開始看的時候我不會呀!所以得學習呀!所以便出現了下文。

一、Java的異常型別

這是一張Java異常體系大致圖:

Java捕獲非檢查異常----UncaughtExceptionHandler的使用

從大體上說,Java中的異常分為兩種:

  • 1、非檢查異常

    非檢查異常為ErrorRuntimeException及其子類,javac在編譯時,不會提示和發現這樣的異常,不要求在程式處理這些異常。所以如果願意,我們可以編寫程式碼處理(使用try…catch…finally)這樣的異常,也可以不處理。對於這些異常,我們應該修正程式碼,而不是去通過異常處理器處理 。這樣的異常發生的原因多半是程式碼寫的有問題。如除0錯誤ArithmeticException,錯誤的強制型別轉換錯誤ClassCastException,陣列索引越界ArrayIndexOutOfBoundsException,使用了空物件NullPointerException等等

  • 2、檢查異常

    檢查異常則是除了ErrorRuntimeException的其它異常。javac強制要求程式設計師為這樣的異常做預備處理工作(使用try…catch…finally或者throws)。在方法中要麼用try-catch語句捕獲它並處理,要麼用throws子句宣告丟擲它,否則編譯不會通過。這樣的異常一般是由程式的執行環境導致的。因為程式可能被執行在各種未知的環境下,而程式設計師無法干預使用者如何使用他編寫的程式,於是程式設計師就應該為這樣的異常時刻準備著。如SQLException , IOException,ClassNotFoundException 等。

總結下:

  • 1、檢查異常,非檢查異常都可以使用try…catch…finally塊捕獲異常
  • 2、非檢查異常屬於執行時異常,編譯器不會提示你進行捕獲

為什麼我要進行總結呢?下面我將說到!

二、UncaughtExceptionHandler的使用

很多時候我們寫完程式碼,執行之後老是會報空指標異常,我們卻沒發現應該去捕獲它。特別是在多執行緒環境中,執行緒類Threadrun()方法是沒有丟擲任何異常資訊的,如果在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"));
複製程式碼

可以看出,如果usernull,則必定也是丟擲空指標異常。很多很多非檢查異常我們都需要儘量避免,在編寫程式碼的時候多考慮是否會出現空指標,是否會出現陣列越界等等情況。

相關文章