java异常处理2(细微细节)

//这次博客是对我所写的java异常处理1(基本技术)的一个补充,如果对java的异常处理机制还未理解请移步https://www.jianshu.com/p/4287c5220c0e阅读。

异常的链化

    在一些大型的,模块化的软件开发中,一旦一个地方发生异常,则如骨牌效应一样,将导致一连串的异常。假设B模块完成自己的逻辑需要调用A模块的方法,如果A模块发生异常,则B也将不能完成而发生异常,但是B在抛出异常时,会将A的异常信息掩盖掉,这将使得异常的根源信息丢失。异常的链化可以将多个模块的异常串联起来,使得异常信息不会丢失。

    异常链化:以一个异常对象为参数构造新的异常对象。新的异对象将包含先前异常的信息。这项技术主要是异常类的一个带Throwable参数的函数来实现的。这个当做参数的异常,我们叫他根源异常(cause)。

    查看Throwable类源码,可以发现里面有一个Throwable字段cause,就是它保存了构造时传递的根源异常参数。这种设计和链表的结点类设计如出一辙,因此形成链也是自然的了。


java异常处理2(细微细节)_第1张图片
Throwable源码


java异常处理2(细微细节)_第2张图片


java异常处理2(细微细节)_第3张图片
一个加法器的示例


java异常处理2(细微细节)_第4张图片
运行结果

自定义异常

    如果要自定义异常类,则扩展Exception类即可,因此这样的自定义异常都属于检查异常(checked exception)。如果要自定义非检查异常,则扩展自RuntimeException。

按照国际惯例,自定义的异常应该总是包含如下的构造函数:

1.一个无参构造函数

2.一个带有String参数的构造函数,并传递给父类的构造函数。

3.一个带有String参数和Throwable参数,并都传递给父类构造函数

4.一个带有Throwable 参数的构造函数,并传递给父类的构造函数。

下面是IOException类的完整源代码,可以借鉴。


java异常处理2(细微细节)_第5张图片
IOException类的完整源代码

异常的注意事项

1、当子类重写父类的带有 throws声明的函数时,其throws声明的异常必须在父类异常的可控范围内——用于处理父类的throws方法的异常处理器,必须也适用于子类的这个带throws方法 。这是为了支持多态。

例如,父类方法throws 的是2个异常,子类就不能throws 3个及以上的异常。父类throws IOException,子类就必须throws IOException或者IOException的子类。

2、Java程序可以是多线程的。每一个线程都是一个独立的执行流,独立的函数调用栈。如果程序只有一个线程,那么没有被任何代码处理的异常 会导致程序终止。如果是多线程的,那么没有被任何代码处理的异常仅仅会导致异常所在的线程结束。

也就是说,Java中的异常是线程独立的,线程的问题应该由线程自己来解决,而不要委托到外部,也不会直接影响到其它线程的执行。

finally块和return


java异常处理2(细微细节)_第6张图片
示例代码


java异常处理2(细微细节)_第7张图片
运行结果

try-catch中的finally会永远执行,就算try中方法体有返回值并正常终结。

但是在异常中还是会有让人想不到的情况发生:

    1、finally中的return 会覆盖 try 或者catch中的返回值。

    2、finally中的return会抑制(消灭)前面try或者catch块中的异常

    3、finally中的异常会覆盖(消灭)前面try或者catch中的异常

为了避免这些情况,因为这些情况出现了会让人很迷惑。在此有几条忠告:

   1、 不要在fianlly中使用return。

    2、不要在finally中抛出异常。

    3、减轻finally的任务,不要在finally中做一些其它的事情,finally块仅仅用来释放资源是最合适的。

    4、将尽量将所有的return写在函数的最后面,而不是try … catch … finally中。

你可能感兴趣的:(java异常处理2(细微细节))