在任何编程语言中,提供应用中错误情景的管理机制都是非常重要的。和其他现代编程语言一样,Java 编程语言提供了基于异常的错误管理机制。当发生错误情况时,Java 将抛出异常类。除了已有的异常类,还可以自定义异常类,以便管理类中产生的错误。
Java 也提供了捕获和处理异常的机制。有一些异常必须被捕获或者通过方法上的throws声明再次抛出,这类异常被称为检查异常。而有一些异常不需要方法上的声明或捕获,这类异常被称为非检查异常。
- 检查异常: 该异常必须在方法的throws语句中声明,或者在方法内部捕获,例如IOException或ClassNotFoundException。
- 非检查异常: 该异常不需要声明或者捕获,例如NumberFormatException。
如果在一个线程对象的run()方法内抛出检查异常,则必须对其进行捕获处理,因为run()方法不接受throws语句。如果一个非检查异常在一个线程对象的run()方法内被抛出,则会默认将异常栈信息打印到控制台,并退出程序。
幸运的是,Java 提供了用于捕获和处理线程对象中抛出的非检查异常机制,以避免程序的结束。
本节将在案例中使用这种机制。
/**
* @Author wj
* @Description 实现一个处理非检查异常的类。
* 该类必须实现UncaughtExceptionHandler接口
* @Date 14:20 2022/5/24
**/public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
/**
* @Author wj
* @Description 用于输出异常和抛出线程信息
* @Date 14:21 2022/5/24
**/ @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());
}
public static class Task implements Runnable {
@Override
public void run() {
//构造异常
int numero = Integer.parseInt("TTT");
}
}
public static void main(String[] args) {
Task task = new Task();
Thread thread = new Thread(task);
//为线程设置非检查异常处理器
thread.setUncaughtExceptionHandler(new ExceptionHandler());
thread.start();
}
}
结果分析
案例运行结果如下图所示。异常被抛出后,异常处理器会将其捕获,然后输出该异常及抛出线程的信息。这些信息均打印到控制台:
当线程中抛出一个异常且未捕获(必须是非检查异常)时,JVM 会检查是否通过相关方法为该线程配置了未捕获异常处理器(UncaughtExceptionHandler)。如果有,那么 JVM 将调用线程对象上相应的方法,并传递抛出的异常作为参数。
如果线程对象没有配置未捕获异常处理器,则 JVM 会在控制台中打印出异常信息栈,然后结束异常抛出线程的执行。
其他说明
Thread类中还定义了另一个用于处理未捕获异常的方法。
即静态方法setDefault- UncaughtExceptionHandler()。该方法可以为应用中所有线程对象设置默认的未捕获异常处理器。
当线程中抛出未捕获异常时,JVM 会为该异常依次查找 3 个可能的处理器。
首先,如本节所介绍的,JVM 会查找线程对象的未捕获异常处理器。如果该处理器不存在,则 JVM 将查找在 1.10 节中介绍的线程组的未捕获异常处理器。如果该处理器仍然不存在,则 JVM 将会查找默认的异常处理器。
当然,如果上述异常处理器都不存在,那么 JVM 会在控制台中打印异常信息栈,然后结束异常抛出线程的执行。