今天在看 Zookeeper
源码的时候发现在其源码中使用到了 UncaughtExceptionHandler
处理非检查异常,关键是刚开始看的时候我不会呀!所以得学习呀!所以便出现了下文。
这是一张Java异常体系大致图:
从大体上说,Java中的异常分为两种:
1、非检查异常
非检查异常为 Error
和 RuntimeException
及其子类, javac
在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。所以如果愿意,我们可以编写代码处理(使用 try…catch…finally
)这样的异常,也可以不处理。对于这些异常,我们应该修正代码,而不是去通过异常处理器处理 。这样的异常发生的原因多半是代码写的有问题。如除 0
错误 ArithmeticException
,错误的强制类型转换错误 ClassCastException
,数组索引越界 ArrayIndexOutOfBoundsException
,使用了空对象 NullPointerException
等等
2、检查异常
检查异常则是除了 Error
和 RuntimeException
的其它异常。javac强制要求程序员为这样的异常做预备处理工作(使用 try…catch…finally
或者 throws
)。在方法中要么用try-catch语句捕获它并处理,要么用 throws
子句声明抛出它,否则编译不会通过。这样的异常一般是由程序的运行环境导致的。因为程序可能被运行在各种未知的环境下,而程序员无法干预用户如何使用他编写的程序,于是程序员就应该为这样的异常时刻准备着。如 SQLException
, IOException
, ClassNotFoundException
等。
总结下:
try…catch…finally
块捕获异常 为什么我要进行总结呢?下面我将说到!
很多时候我们写完代码,运行之后老是会报空指针异常,我们却没发现应该去捕获它。特别是在多线程环境中,线程类 Thread
的 run()
方法是没有抛出任何异常信息的,如果在 run()
方法中报一个空指针异常,我们却没有捕获它,那后果将会是使线程中断,线程中断可能并不是我们想看到的结果。
最可气的是,子线程抛出的异常信息,在其主线程同样是捕获不到的。下面举例子演示下:
public class NoCaughtThread { public static void main(String[] args) { // 捕获不到 try { Thread thread = new Thread(new Task()); thread.start(); } catch (Exception e) { e.printStackTrace(); } } } class Task implements Runnable { @Override public void run() { throw new NullPointerException(); } }
程序运行结果:
Exception in thread "Thread-0" java.lang.NullPointerException at org.apache.zookeeper.server.quorum.Task.run(NoCaughtThread.java:21) at java.lang.Thread.run(Unknown Source)
为了处理这些未捕获的异常,jdk自带了 UncaughtExceptionHandler
类进行处理,实例如下:
import java.lang.Thread.UncaughtExceptionHandler; public class NoCaughtThread { public static void main(String[] args) { // 设置未捕获异常处理器,这里是默认的,当然你可以自己新建一个类,然后实现UncaughtExceptionHandler接口即可 Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { System.err.println("程序抛出了一个异常,异常类型为 : " + e); } }); Thread thread = new Thread(new Task()); thread.start(); } } class Task implements Runnable { @Override public void run() { throw new NullPointerException(); } }
程序输出:
程序抛出了一个异常,异常类型为 : java.lang.NullPointerException
总结一下,归根到底,我觉得之所以需要使用到 UncaughtExceptionHandler
,很多情况下都是因为我们自己代码写的不好,不够严谨,或者说我们在写代码的时候没有考虑周到。比如最常见的就是抛出空指针 NullPointerException
,请看下面代码:
Map<String, String> map = null; System.err.println(map.get("name"));
上面的代码必定抛出空指针异常,你可能会说,哇,谁这么笨,这么明显声明 map=null
,好,为了使你信服,我决定打个响指:
// 从数据库查找一个用户 User user = userDao.getUser(12); System.err.println(user.getUserName("name"));
可以看出,如果 user
为 null
,则必定也是抛出空指针异常。很多很多非检查异常我们都需要尽量避免,在编写代码的时候多考虑是否会出现空指针,是否会出现数组越界等等情况。