Java还提供一种捕获和处理这些异常的机制。方法中必须被捕获或者使用throws语句再次抛出的异常是受检异常,无须指定或捕获的异常称为非受检异常。
- 受检异常 : 必须在方法的throws语句中指定或内部捕获的异常,例如,IOException 或者 ClassNotFoundException。
- 非受检异常 : 无须指定或捕获的异常,例如NumberFormatException。
当受检异常在线程对象的run()方法中被抛出来时,由于run()方法不接受throws语句,所以必须捕获和处理这些异常。当非受检异常在线程对象的run()方法中被抛出来时,默认行为是在控制台输出堆栈跟踪,同时退出程序。
不过Java提供了一种机制来捕获和处理线程对象抛出的非受控异常,避免终止程序。
在本节中,通过范例学习这种机制原理。
准备工作
本范例通过Eclipse开发工具实现。如果使用诸如NetBeans的开发工具,打开并创建一个新的Java项目。
实现过程
通过如下步骤完成范例:
-
首先,实现一个类来处理非受检异常。这个类必须实现UncaughtExceptionHandler接口和接口定义的uncaughtException()方法,此方法依附Thread类。在本范例中,此类命名为ExceptionHandler,然后实现方法记录抛出的Exception和Thread信息。代码如下:
public class ExceptionHandler implements UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { System.out.printf("An exception has been captured\n"); System.out.printf("Thread: %s\n", t.getId()); System.out.printf("Exception: %s: %s\n", e.getClass().getName(), e.getMessage()); System.out.printf("Stack Trace: \n"); e.printStackTrace(System.out); System.out.printf("Thread status: %s\n", t.getState()); } }
-
现在,实现一个类抛出非受检异常。此类命名为Task,指定其实现Runnable接口和run()方法。强制其报错,例如,尝试将字符串转换成整型:
public class Task implements Runnable { @Override public void run() { int numero = Integer.parseInt("TTT"); } }
-
现在实现主类,包含main()方法的Main类:
public class Main { public static void main(String[] args) {
-
创建Task对象,以及运行此对象的线程。使用setUncaughtExceptionHandler()设置非受控异常处理器,开始执行线程:
Task task = new Task(); Thread thread = new Thread(task); thread.setUncaughtExceptionHandler(new ExceptionHandler()); thread.start(); } }
-
运行程序,查看结果。
工作原理
在下图中显示执行范例输出的结果。通过控制器抛出和捕获的异常记录抛出的Exception和Thread信息,然后输出到控制台上:
当线程中被抛出一个异常并且始终未捕获(非受检异常),Java虚拟机检查线程是否有通过匹配方法设置的非捕获异常控制器。如果有,Java虚拟机用Thread对象和Exception参数调用此方法。
如果线程没有非捕获异常处理器,Java虚拟机会在控制台输出堆栈跟踪,并且终止执行已经抛出异常的线程。
扩展学习
Thread类中的静态方法setDefaultUncaughtExcptionHandler()也与非捕获异常处理有关,此方法为应用中的所有线程对象建立一个异常处理器。
当线程中抛出一个非捕获异常时,Java虚拟机寻找异常中三个可能的处理器。
首先是寻找线程对象的非捕获异常处理器,也就是本节中学到的。如果此处理器不存在,Java虚拟机寻找ThreadGroup类中的非捕获异常控制器,这个类在“线程组中分组线程、处理非受控异常”小节中学习。如果此方法也不存在,Java虚拟机会寻找默认的非捕获异常处理器。
如果三种处理器均不存在,Java虚拟器在控制台输出异常的堆栈跟踪,并且终止执行已经抛出异常的线程。
更多关注
- 本章中“工厂模式创建线程”小节。