重谈java异常-Throwable、ERROR、Exception、RuntimeException

原创
2016/11/30 16:34
阅读数 575

Java中,异常处理,是谁也避不开的,而且,其实呢,java中的异常看似繁琐,其实认真起来,是很规范的处理可能发生的错误的一种有效的方式。

Java中异常的集成关系: Throwable : 所有异常均集成自这个接口 Error 和 Exception : Throwable 接口在JDK中只有两个子类,分别是Error 和Exception。

Error,代表有严重的事情发生了,不可恢复 Exception(排除RuntimeException),代表应用程序应该捕获,并且可以被处理的异常,又被称为checked exception。 RuntimeException,是Exception的子类,但是该类异常一般特指运行时出现的异常,并非代码错误。比如NullPointerException是运行时某些不应该出现的特殊场景抛出的异常。该异常原则上是可以不进行捕获处理的。


这里,会有人问,那,哪些异常我们需要处理,哪些不需要处理呢?什么时候用catch,什么时候不要用呢?

个人见解,这里还是需要看业务!业务如山! 不是所有人都能够按照严格的异常规范去完成异常的抛出和处理,但是一个基本原则是有的,显示抛出的异常,都是需要处理的,这里的处理可以是再次抛出、根据问题进行修正流程和业务、吞掉异常,总之,是有一个处理方案的。

上面其实是不规范的写法造成的我们必须要进行重新审视并处理的方法。 而Java Lanuage Spec 7 中也提到:Error继承自Throwable而不是继承自Exception,是为了方便程序可以使用 “catch (Exception)“来捕捉异常而不会把Error也捕捉在内,因为Exception发生后可以进行一些恢复工作的,但是Error发生后一般是不可恢复的。

The class Error is a separate subclass ofThrowable, distinct from Exception in the class hierarchy, to allow programs to use the idiom “} catch (Exception e) { " (§11.2.3) to catch all exceptions from which recovery may be possible without catching errors from which recovery is typically not possible.

这是一个标准的提法,也是我们应该遵循的做法,catch Exception而不是catch Throwable 或者catch Error


PS : 引申一下,我们在代码中如何构建自己的一个异常体系呢? 个人观点,简单遵循以下几个原则即可:

  • 自定义业务异常,不要全部使用Exception等JDK提供的异常,重复通过异常名称进行分类,方便后续处理
  • 所有自定义异常都继承自Exception或者RuntimeException,JDK已经把异常分的很清楚了,不必要再继承Throwable自定义特殊异常。
  • 异常处理,能够在本方法中处理的,尽量不要再抛出异常让外层处理,一旦抛出异常,很可能,这个异常就没有人处理了,或者,最终只是个日志。
  • 如果做的是一个框架或者库,那么好的异常结构让使用者会非常舒服。

补充:关于异常的性能 大多数人都会听说过,异常是非常耗费资源的。但是,Java既然将异常设计的这么好,我们就为了性能而不用了吗?

我们只要知道,哪里会出现性能问题,尽量避免发生就可以了。毕竟,什么事异常?字面上讲,就是错误的东西,如果程序在执行过程中一直会有异常出现,那,这个代码是不是应该考虑写错了。

执行一个catch代码块和抛出一个异常花费是很高的,这个过程中的性能损耗主要是由于当创建一个异常时要获得线程栈的一个快照。抛出异常首先要创建一个新的对象Throwable类的构造函数调用名为fillInStackTrace的方法,fillInStackTrace方法检查堆栈,收集调用跟踪信息。由于在处理过程中创建了一个新的对象,所以说只要有异常被抛出,JVM就必须调整调用堆栈,系统资源开销也就增大了。

    public synchronized Throwable fillInStackTrace() {
        if (stackTrace != null ||
            backtrace != null /* Out of protocol state */ ) {
            fillInStackTrace(0);
            stackTrace = UNASSIGNED_STACK;
        }
        return this;
    }

    private native Throwable fillInStackTrace(int dummy);

我们可以看到,JDK的Throwable.fillInStackTrace方法,最终调用的是一个native方法进行处理的。在不了解Hotspot的具体处理方式的前提下,我们可以想象,Java对这方面的处理,应该是越来越快的。:)

    在写这篇文章的时候,我在看一篇文章:http://code.oneapm.com/pm/2015/05/12/Java-exception/,里面的实验跟着去做,可以得出跟作者一致的答案,侧面印证了我们上面的结论。

那,我们该如何做好异常的使用呢? 总结几个简单原则:

  • cache块尽量用1个,不要分多个
  • 异常只用于错误,不要用来控制流程
  • 只处理自己能够处理的

好久不写文章了,有些凌乱,恢复恢复状态 ^-^

展开阅读全文
加载中
点击加入讨论🔥(1) 发布并加入讨论🔥
打赏
1 评论
0 收藏
0
分享
返回顶部
顶部