使用UncaughtExceptionHandler捕获运行时异常

前面我们知道Exceptions分为可检查异常(checked exceptions)和运行时异常(runtime exception)。具体参照文章Java异常处理手册和最佳实践,对于可检查异常,我们必须对它进行处理,要么捕获要么在方法上使用throws抛给调用者;运行时异常很大可能是因为程序员造成的,它往往是可以避免的,它是无法预测的,所以不需要进行捕获,也不需要在方法上添加throws关键字来标识,那么对于这种无法预测的异常,如果我们希望能够对这个异常进行捕获该怎么做?

Java为我们提供了一个机制,用来捕获并处理在一个线程对象中抛出的未检测异常,以避免程序终止。我们可以通过UncaughtExceptionHandler来实现这种机制。

下面我们来举个运行时异常的情况。

import java.lang.Thread.UncaughtExceptionHandler;

public class StringTest {
    public static void main(String[] args) {
        System.out.println(Integer.parseInt("123"));
        System.out.println(Integer.parseInt("456"));
        System.out.println(Integer.parseInt("XYZ")); //这里有NumberFormatException异常
        System.out.println(Integer.parseInt("789"));
    }

}

上面的代码System.out.println(Integer.parseInt(“XYZ”))代码会出现运行时异常,它会报出一个NumberFormatException异常,就是因为XYZ不是一个数字,我们来看看运行结果。

123
456
Exception in thread "main" java.lang.NumberFormatException: For input string: "XYZ"
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:580)
    at java.lang.Integer.parseInt(Integer.java:615)
    at StringTest.main(StringTest.java:10)

它默认的将堆栈跟踪信息写到控制台中,对于这样的运行时异常,我们如果希望捕获它,可以使用UncaughtExceptionHandler,下面我们来看看使用UncaughtExceptionHandler后的代码。

import java.lang.Thread.UncaughtExceptionHandler;

public class StringTest {
    public static void main(String[] args) {
        Thread.setDefaultUncaughtExceptionHandler(new MyCrashHandler());
        System.out.println(Integer.parseInt("123"));
        System.out.println(Integer.parseInt("456"));
        System.out.println(Integer.parseInt("XYZ")); //这里有NumberFormatException异常
        System.out.println(Integer.parseInt("789"));
    }

}

class MyCrashHandler implements UncaughtExceptionHandler {

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("捕获到异常:" + e.getMessage());
    }

}

我们来看看运行结果:

123
456
捕获到异常:For input string: "XYZ"

可以很清楚的看到这个异常确实被捕获到了,并且这个异常的处理方法是UncaughtExceptionHandler里面的uncaughtException方法,我们只需要重写这个方法进行相应的操作就可以了。

我们可以注意到,之所以我们可以捕获这个异常,是因为我们使用到了Thread.setUncaughtExceptionHandler(new MyCrashHandler())方法,这个方法的作用就是设置系统的默认异常处理器,使用这样方法之后,我们程序中所有的线程的运行时出现异常都会被我们注册的这个异常处理器进行处理。

下面我们来举个例子:

import java.lang.Thread.UncaughtExceptionHandler;

public class StringTest {
    public static void main(String[] args) {
        Thread.setDefaultUncaughtExceptionHandler(new MyCrashHandler());

        //单独起动一个线程,在这个线程中出现NumberFormatException异常
        new Thread(new Runnable(){

            @Override
            public void run() {
                System.out.println(Integer.parseInt("123"));
                System.out.println(Integer.parseInt("456"));
                System.out.println(Integer.parseInt("XYZ")); //这里有NumberFormatException异常
                System.out.println(Integer.parseInt("789"));

            }

        }).start();


        //在主线程中出现NumberFormatException异常
        System.out.println(Integer.parseInt("123"));
        System.out.println(Integer.parseInt("456"));
        System.out.println(Integer.parseInt("")); //这里有NumberFormatException异常
        System.out.println(Integer.parseInt("789"));
    }

}

class MyCrashHandler implements UncaughtExceptionHandler {

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("捕获到异常:" + e.getMessage());
    }

}

上面我们看到我们单独创建了一个线程,这个线程会出现运行时异常,另外在main线程中也会出现运行时异常。

下面我们来看看运行结果。

123
456
捕获到异常:For input string: ""
123
456
捕获到异常:For input string: "XYZ"

从运行结果,我们可以很清楚的看到,两个线程的异常都被捕获到了,这样我们就可以知道Thread.setUncaughtExceptionHandler(new MyCrashHandler())方法的作用了。

如果我们只希望为某个指定的线程来注册一个异常处理器,而不是所有的,该怎么办,很简单,我们只需要对指定的线程来调用setUncaughtExceptionHandler方法就可以了。

下面我们来对上面的代码进行修改,把异常处理器的注册进行修改。

import java.lang.Thread.UncaughtExceptionHandler;

public class StringTest {
    public static void main(String[] args) {

        //单独起动一个线程,在这个线程中出现NumberFormatException异常
        new Thread(new Runnable(){

            @Override
            public void run() {
                Thread.currentThread().setDefaultUncaughtExceptionHandler(new MyCrashHandler());
                System.out.println(Integer.parseInt("123"));
                System.out.println(Integer.parseInt("456"));
                System.out.println(Integer.parseInt("XYZ")); //这里有NumberFormatException异常
                System.out.println(Integer.parseInt("789"));

            }

        }).start();


        //在主线程中出现NumberFormatException异常
        System.out.println(Integer.parseInt("123"));
        System.out.println(Integer.parseInt("456"));
        System.out.println(Integer.parseInt("")); //这里有NumberFormatException异常
        System.out.println(Integer.parseInt("789"));
    }

}

class MyCrashHandler implements UncaughtExceptionHandler {

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("捕获到异常:" + e.getMessage());
    }

}

从上面可以看到,我们只是在我们创建的线程中对当前线程注册了一个异常处理器。我们来看看运行结果。

123
456
Exception in thread "main" java.lang.NumberFormatException: For input string: ""
    at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
    at java.lang.Integer.parseInt(Integer.java:592)
    at java.lang.Integer.parseInt(Integer.java:615)
    at StringTest.main(StringTest.java:27)
123
456
捕获到异常:For input string: "XYZ"

我们可以很清楚的看到,只有我们创建的线程中出现的异常被处理,main线程中的异常没有进行处理,而是默认的将堆栈跟踪信息写到控制台中。

下面我们来说说这样捕获运行时异常有什么作用呢?

在Android开发中,我们都知道很多应用都有Crash上报,对于用户手机发生异常的时候,我们如果对用户手机的异常进行捕获,然后上报到我们的服务器,方便我们及时的进行处理。我们一般使用的就是这样方法,我们会使用Thread.setUncaughtExceptionHandler(new MyCrashHandler())为我们应用的所有线程指定异常处理器,当我们的程序出现运行时异常的时候,我们的异常处理器就可以捕获到这个异常,也就回调uncaughtException方法,这样我们就可以得到这样异常信息,然后将这个异常进行服务器上报。具体的做法可以看看大神写的这篇文章Android程序Crash时的异常上报

参考文章:

How to Restart Thread Using UncaughtExceptionHandler

中文翻译:

使用UncaughtExceptionHandler重启线程

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java 中,我们可以通过捕获全局异常来处理程序中未被处理的异常。全局异常处理器可以处理任何未被捕获异常,无论是在应用程序的主线程还是在其他线程中发生。 下面是一个简单的示例代码,演示如何使用全局异常处理器: ```java public class GlobalExceptionHandler implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { // 处理异常的代码 System.out.println("An exception occurred: " + e.getMessage()); } public static void main(String[] args) { // 设置全局异常处理器 Thread.setDefaultUncaughtExceptionHandler(new GlobalExceptionHandler()); // 抛出一个异常 throw new RuntimeException("Oops, something went wrong!"); } } ``` 在上面的代码中,我们定义了一个名为 `GlobalExceptionHandler` 的类,它实现了 `Thread.UncaughtExceptionHandler` 接口,并重写了其中的 `uncaughtException` 方法。在 `main` 方法中,我们通过调用 `Thread.setDefaultUncaughtExceptionHandler` 方法来设置全局异常处理器为 `GlobalExceptionHandler` 类的实例。接下来,我们抛出一个运行时异常,这会导致全局异常处理器被调用。在这个处理器中,我们可以编写代码来处理异常,例如记录日志、发送错误报告等等。 需要注意的是,全局异常处理器只能处理未被捕获异常。如果异常已经被 `try-catch` 块捕获,那么全局异常处理器将不会被调用。因此,在编写代码时,我们应该尽可能地使用 `try-catch` 块来捕获异常,并在必要时使用全局异常处理器来处理未被捕获异常
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值