原文地址:http://Java.sun.com/developer/JDCTechTips/2006/tt0211.Html#2
日期:<?XML:namespace prefix = st1 ns = "urn:schemas-microsoft-com:Office:smarttags" />
UncaughtExceptionHandler名字意味著處理未捕捉的異常。更明確的說,它處理未捕捉的運行時異常。Java編譯器要求處理所有非運行時異常,否則程序不能編譯通過。這裡“處理”的是方法裡throws子句聲明的異常或在try-catch塊裡的catch子句的異常。
在下面的示例中,讓我們關注兩個異常:FileNotFoundException和ArithmeticException。假如使用一個不正確的String或File參數值來構造FileReader時,將會拋出FileNotFoundException異常。當你調用這些構造函數時,編譯器要求你處理每一個可能拋出異常的子句:
FileReader input;
String filename = ...;
try {
input = new FileReader(filename);
} catch (FileNotFoundException e) {
processMissingFile(filename, e);
}
我們再來比較一下,ArithmeticException是一個運行時異常。Java編程語言規范(對於編譯器而言)不要求處理運行時異常。因此在下面的循環中,從10到0都會被100除,而在最後一次循環中將拋出ArithmeticException異常:
for (int i=10; i >= 0; i--) {
int div = 100 / i;
}
Exception in thread "main" java.lang.ArithmeticException: /
by zero
at Test.main(Test.java:4)
輸出堆棧信息顯示出缺省未捕捉的異常內容。通常說來,這樣的缺省行為已經足夠了,不過有時也不行。設想你想在彈出式窗口輸出堆棧信息,來替代在系統控制台輸出該信息。假如你自己設置一個缺省的未捕捉異常處理,你就可以得到這樣的效果。
這裡有三個最新的處理未捕捉異常的方法。第一,你可以調用Thread的setUncaughtExceptionHandler()方法。這個方法答應你在非凡的線程裡定制行為。第二,你可以定義你自己的ThreadGroup並改變行為使每個線程都從這個組中創建並重寫它們的uncaughtException()方法。第三,你可以設置缺省行為讓所有線程調用Thread的靜態方法setDefaultUncaughtExceptionHandler()。
Thread的setUncaughtExceptionHandler()和setDefaultUncaughtExceptionHandler()方法都需要實現UncaughtExceptionHandler接口作為參數。該接口是Thread類的內部接口,因此它的全名是Thread.UncaughtExceptionHandler。該接口的唯一方法是:
void uncaughtException(Thread t, Throwable e)
當你實現了uncaughtException方法或實現了該接口或重寫ThreadGroup的方法,你都可以定制其行為。在下面的示例中,我們實現了UncaughtExceptionHandler,無論運行時異常何時發生,堆棧信息都會顯示在窗口的文本區域裡。你可以在兩個異常間關閉窗口。當下次異常發生時,窗口會再次出現在其他窗口之前。
import java.awt.*;
import java.io.*;
import javax.swing.*;
public class StackWindow extends JFrame
implements Thread.UncaughtExceptionHandler {
private JTextArea textArea;
public StackWindow(
String title, final int width, final int height) {
super(title);
setSize(width, height);
textArea = new JTextArea();
JScrollPane pane = new JScrollPane(textArea);
textArea.setEditable(false);
getContentPane().add(pane);
}
public void uncaughtException(Thread t, Throwable e) {
addStackInfo(e);
}
public void addStackInfo(final Throwable t) {
EventQueue.invokeLater(new Runnable() {
public void run() {
// Bring window to foreground
setVisible(true);
toFront();
// Convert stack dump to string
StringWriter sw = new StringWriter();
PrintWriter out = new PrintWriter(sw);
t.printStackTrace(out);
// Add string to end of text area
textArea.append(sw.toString());
}
});
}
}
要測試這個處理,你需要一個程序來設置這個處理並拋出一些運行時異常。在下面的程序中,DumpTeat將會這樣做。為了簡單,DumpTest僅僅產生兩個異常。假如要拋出更多的異常,你可以自由的在程序裡添加更多的錯誤代碼。程序在第一個異常顯示後會暫停,你可以在異常間關閉異常堆棧窗口。
import java.io.*;
public class DumpTest {
public static void main(final String args[])
throws Exception {
Thread.UncaughtExceptionHandler handler =
new StackWindow("Show Exception Stack", 400, 200);
Thread.setDefaultUncaughtExceptionHandler(handler);
new Thread() {
public void run() {
System.out.println(1 / 0);
}
}.start();
BufferedReader br =
new BufferedReader(new InputStreamReader(System.in));
System.out.print("Press Enter for next exception");
br.readLine();
new Thread() {
public void run() {
System.out.println(args[0]);
}
}.start();
System.out.print("Press Enter to end");
br.readLine();
System.exit(0);
}
}
編譯StackWindow和DumpTest。當你運行DumpTest時,在控制台裡會看見下面的內容:
> java DumpTest
Press Enter for next exception
你將看見堆棧信息顯示在窗口裡的文本區域裡。
按下Enter,在控制台裡回看見下面的內容:
Press Enter to end
你可以在窗口的文本區裡看見另一個堆棧信息。
雖然你可以認為這些是處理未捕捉的異常的所有方法,其實還有更多的方法。模式對話框要求有它自己的事件線程,因為它自己非捕捉處理。系統屬性sun.awt.exception.handler覆蓋了所有容器,但不是好的文檔。RFE(增強型請求)提交了可擴展的屬性到正式的API中。
除Best Practices in Exception Handling外,2002年5月的技巧文章StackTraceElements也是一篇重要的參考。也可以看The Java Tutorial的Handling Errors Using Exception課程。