异常分为可检查异常(checked exception)和不可检查异常(unchecked exception);对于可检查异常,必须在代码中进行显示的处理,要么通过throws关键字抛出,要么通过try{ }catch块处理。而对于不可检查异常(运行时发生的异常),不需要显示的处理。也就是说,不可检查异常不需要throws关键字抛出,也不需要catch块处理,前提是你不想处理。 示例代码:
public class RunnableThread implements Runnable{
private int number;
public RunnableThread(int number) {
this.number = number;
}
@Override
public void run() {
//throw new RuntimeException(); //不可检查异常(运行时异常),可以编译通过
throw new FileNotFoundException();//可检查异常,编译不通过
}
}
public class RunnableThread implements Runnable{
private int number;
public RunnableThread(int number) {
this.number = number;
}
@Override
public void run() {
//throw new RuntimeException();
try {
throw new FileNotFoundException(); //可检查异常,必须显示的处理
} catch (FileNotFoundException e) {
e.printStackTrace();
}
try {
throwException(); //throwException将自己方法体内部的异常直接抛出,然后run方法不能直接抛出异常,所以必须使用catch块处理
} catch (Exception e) {
e.printStackTrace();
}
}
public void throwException() throws Exception{ //将方法体内部的异常直接抛出
throw new NullPointerException();
}
}
再看一段示例代码:
public class ThreadTest13 {
public static void main(String[] args) throws Exception {
RunnableThread runnableThread = new RunnableThread(1);
Thread thread1 = new Thread(runnableThread);
try{
thread1.start();
}catch(Exception e){
System.out.println("异常被捕获!!!"); //这里虽然声明了catch块,但是是捕捉不到线程中抛出的异常的
}
}
}
public class RunnableThread implements Runnable{
private int number;
public RunnableThread(int number) {
this.number = number;
}
@Override
public void run() {
int number = 1/0; //0不能做被除数,这里会抛出运行时异常:ArithmeticException
}
}
//输出结果(没有打印出“异常被捕获”):
//Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
从上面的两段代码中可以得出两个结论:1、run方法不能直接显示的抛出非运行时异常;2、run方法中的运行时异常不能被主线程捕获。 那该如何处理线程中抛出的异常呢?
一、线程中运行时异常的处理
JDK中有一个UncaughtExceptionHandler接口,叫做异常捕捉器。主要是用来捕获线程中抛出的异常的。通过示例代码看一下如何使用这个接口:
public class ExceptionHandler implements UncaughtExceptionHandler{ //创建一个异常处理器类
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("异常捕获成功!!");
}
}
public class RunnableThread implements Runnable{
private int number;
public RunnableThread(int number) {
this.number = number;
}
@Override
public void run() {
int number = 1/0; //0不能是做被除数,这里会抛出运行时异常:ArithmeticException
}
}
public class ThreadTest13 {
public static void main(String[] args) throws Exception {
RunnableThread runnableThread = new RunnableThread(1);
Thread thread1 = new Thread(runnableThread);
thread1.setUncaughtExceptionHandler(new ExceptionHandler()); //创建一个异常处理实例,并将其加入线程中。加入的时机是:异常发生之前。即setUncaughtExceptionHandler方法可以在start方法之后,抛出异常之前执行,也可以捕捉到异常。
thread1.start();
}
}
//输出结果:异常捕获成功!!
上面的代码中已经成功的捕获了异常。
对于线程异常的处理还有两种辅助的方式:1、如果线程不传入异常捕捉器,就使用默认的异常捕捉器;2、线程组(多个线程的集合)的异常处理。示例代码:
//线程实例对象级别的处理器
public class ExceptionHandler implements UncaughtExceptionHandler{ //创建一个异常处理器类
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("异常捕获成功!!");
}
}
//类级别的异常处理器
public class ThreadClassExceptionHandler implements UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println(t.getName()+" 线程实例,异常被Thread类捕获!!");
}
}
//线程组级别的异常处理
public class ThreadGroupException extends ThreadGroup{ //处理方式有些不同,是重写uncaughtException方法
public ThreadGroupException(String name){
super(name);
}
@Override
public void uncaughtException(Thread t, Throwable e) { //线程组异常处理
System.out.println(t.getName()+" 线程实例,异常被ThreadGroup捕获!!");
}
}
public class RunnableThread implements Runnable{
private int number;
public RunnableThread(int number) {
this.number = number;
}
@Override
public void run() {
int number = 1/0; //0不能是做被除数,这里会抛出运行时异常:ArithmeticException
}
}
public class ThreadTest13 {
public static void main(String[] args) throws Exception {
Thread.setDefaultUncaughtExceptionHandler(new ThreadClassExceptionHandler());//这个是Thread类级别的处理器,所有线程实例都可以使用
ThreadGroupException threadGroup = new ThreadGroupException("threadGroup"); //创建线程组
RunnableThread runnableThread = new RunnableThread(1);
Thread thread1 = new Thread(threadGroup,runnableThread);//将thread1线程加入线程组中
thread1.setName("Thread-1");
thread1.setUncaughtExceptionHandler(new ExceptionHandler()); //传入异常处理器
thread1.start();
Thread thread2 = new Thread(threadGroup,runnableThread); //将thread2线程加入线程组中。没有传入异常处理器
thread2.setName("Thread-2");
thread2.start();
}
}
//输出结果:
//Thread-2 线程实例,异常被ThreadGroup捕获!!
//Thread-1 线程实例,异常被捕获!!
从代码中可以看到:
1、线程实例对象有自己的异常处理器时,会优先被调用;
2、线程实例对象没有自己的处理器时,但线程所在线程组重新覆写了异常处理方法,此时会调用线程组的异常处理方法;
3、当线程实例对象没有自己的处理器,且所在线程组中也没有重新定义异常处理方法,则会调用Thread类的默认异常处理器(这里可以通过把ThreadGroupException中的uncaughtException注释掉进行验证),如果有默认的处理器的话。如果没有默认的处理器,则会直接抛出异常。
所以得到的异常处理器调用顺序是:线程对象 > 线程组 > Thread
二、线程中非运行时异常的处理
在run方法中, 对于非运行时异常的处理,除了可以使用catch块来处理之外,还可以将非运行时异常转换成运行时异常抛出。示例代码;
//异常处理器
public class ExceptionHandler implements UncaughtExceptionHandler{
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println(t.getName()+" 线程实例,异常被捕获!!");
}
}
public class RunnableThread implements Runnable{
private int number;
public RunnableThread(int number) {
this.number = number;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
if("Thread-1".equals(name)){ //Thread-1执行抛出异常
int number = 1/0; //0不能是做被除数,这里会抛出运行时异常:ArithmeticException
}
if("Thread-2".equals(name)){//Thread-2执行抛出异常
try {
throw new FileNotFoundException();
} catch (FileNotFoundException e) {
throw new RuntimeException(e); //转换成运行时异常抛出
}
}
if("Thread-3".equals(name)){ //Thread-3执行抛出异常
try {
throwException();
} catch (Exception e) {
throw new RuntimeException(e);//转换成运行时异常抛出
}
}
}
public void throwException() throws Exception{
throw new IOException();
}
}
public class ThreadTest13 {
public static void main(String[] args) throws Exception {
RunnableThread runnableThread = new RunnableThread(1);
Thread thread1 = new Thread(runnableThread);
thread1.setName("Thread-1");
thread1.setUncaughtExceptionHandler(new ExceptionHandler()); //传入异常处理器
thread1.start();
Thread thread2 = new Thread(runnableThread);
thread2.setUncaughtExceptionHandler(new ExceptionHandler()); //传入异常处理器
thread2.setName("Thread-2");
thread2.start();
Thread thread3 = new Thread(runnableThread);
thread3.setUncaughtExceptionHandler(new ExceptionHandler()); //传入异常处理器
thread3.setName("Thread-3");
thread3.start();
}
}
//输出结果:
//Thread-2 线程实例,异常被捕获!!
//Thread-3 线程实例,异常被捕获!!
//Thread-1 线程实例,异常被捕获!!