Java学习之异常处理

时间:2022-12-12 19:44:17

版权声明:原创不易,转载请注明转自http://blog.csdn.net/weewqrer/article/details/51913194


异常恢复是提高鲁棒性最重要的方法。

前言:这篇文章是我自己的笔记,基本上是我翻译的《Thinking in Java》第12章,翻译不是目的,目的是想写一遍印象深刻,因为有些句子翻译不好,就抄了原文。。。

如果有人觉得文章中夹杂着英文很别扭的话,留言,然后我改。。。

基本概念

异常条件(exceptional conditions)是一个可以阻止程序继续执行的问题。出现了异常条件,你无法继续执行,因为在这个上下文中你没有处理这个问题的足够信息,你只能做的就是跳出当前的上下文环境,并且把问题提交给上一层。这就是抛出异常时所发生的事情。

当抛出一个异常时,会发生这么几件事情 : 首先,就像创建其它对象一样,在堆上用new创建一个异常对象;然后,当前的执行路径被终止,从当前的环境中弹出异常对象的引用;此时,异常处理机制接管过来,并且开始寻找合适的地方执行程序,而这个合适的地方就是异常处理程序(exception handler),它的工作就是从错误状态中恢复,使程序要么换一种方式运行,要么继续执行。

一个简单的例子,引用没有初始化:

if(t == null) throw new NullPointerException();
  • 1
  • 2
Java学习之异常处理
  • 1
  • 2

上面这个程序抛出了一个异常,于是在当前环境上就不必再操心了,它会在别的地方得到处理。

异常机制使得我们可以把每件事当作一个事务来处理,而异常机制可以守护这些事务,“……在分布式计算中我们需要异常机制作为最基本的保障,某项事务如果出了什么错误,可以放弃整个计算”。

我们还可以把异常看作是内建的恢复(undo)系统,因为(在细心地使用下)我们在程序中可以拥有各种不同的恢复点,如果程序的某部分失败了,异常将“恢复”到程序中某个已知的稳定点上。

异常最重要的一点是,如果程序发生了什么错误的话,异常会阻止程序沿着原来的路径走下去。这在C和C++中确实是个问题,尤其是C语言中,如果发生了错误,没办法阻止程序继续沿着原来的路走下去,所以有可能忽视这个错误很长时间从而陷入了完全错误的状态中。

异常使得我们(如果没有其他手段)强制程序停止运行,并告诉我们出现了什么问题,或者(理想状态下)强制程序处理问题,并返回到稳定的状态。

异常的参数

创建异常对象有两种:带参和不带参的,不带参的在上面已经见过了,带参的看下面这个例子:

throw new new NullPointerException("t == null");
  • 1
Java学习之异常处理
  • 1

也可以从作用域(scope)中抛出异常,throw关键字抛出异常,简单的来看就是从函数或作用域(scope)中“返回”,无论是从哪里抛出异常,都意味着从这个函数或作用域退出。

另外,你可以抛出任何类型的Throwable,这是异常的基类。通常对于不同类型的错误,要抛出相应的异常。错误信息可以保存在异常对象内部或者用异常类的名称来暗示。上一层通过这些消息决定如何处理异常。(通常,把信息只放在异常的类型中,除此之外不包含任何有意义的内容。)

捕获一个异常

要理解如何捕获异常,必须得先理解监控区域(guarded region)的概念,就是可能会产生异常的一段代码并且紧接着一段处理异常的代码。

try块

如果你在方法的内部,并且抛出一个异常(或者你在这个方法中调用了另一个方法,后者抛出异常),你所在的方法会在抛出异常的过程中退出。如果你不想在抛出异常的过程中退出(throw to exit)那个方法,你可以在那个方法中设置一个特殊块来捕获那个异常。因为在这个块里“尝试”各种(可能产生异常的)方法调用,所以称为try块。如下:

try{ //code that might generate exception }
  • 1
  • 2
  • 3
Java学习之异常处理
  • 1
  • 2
  • 3

异常处理程序 Exception handlers

当然,被抛出的异常必须在某地方得到处理。这个地方就是异常处理程序,并且针对你想捕获的每个异常,得有一个。异常处理程序紧跟try块之后,如下:

try{ //code that might generate exception }catch(Type1 id1){ //处理类型Type1的异常 }catch(Type2 id2){ //…… }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
Java学习之异常处理
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

每个catch子句就像一个接收且仅接收一个特殊类型的参数的方法,可以在处理程序的内部使用标识符(id1, id2),也可以不使用,因为异常的类型已经给出足够的信息了,但是标识符不可以省略。

异常被抛出后,异常机制会搜寻参数与异常类型相匹配的第一个处理程序。然后进入catch子句执行,此时认为异常得到了处理。一旦catch子句结束,则处理程序的查找过程结束。这跟switch不一样,switch语句需要在每个case后面跟一个break,以避免执行后续的case子句。注意在try块的内部,许多不同的方法调用可能会产生类型相同的异常,而你只需要提供一个针对此类型的异常处理程序。

termination vs. resumption
终止与恢复
在异常处理理论中有两种基本的模型,java支持终止模型(java和c++都支持这种模型),这种模型假定发生的错误太严重了,已经到了没办法恢复的地步。另一种是可采取的办法是恢复,这意味着异常处理程序想做点什么来纠正错误,然后再尝试一次刚才发生异常的方法。如果你想恢复,就意味着你在异常处理后再接着执行原来的程序。

在Java中如果想恢复的话,当遇到错误时不要抛出异常,而是,调用一个可以修复这个错误的方法。或者,把try块放到while中,一直尝试直到结果满意。

从历史观点上说,编程人员一开始使用恢复的方法,但是却最终都直接跳过恢复而使用了终止模型。尽管恢复很迷人,但是在实践上并不好用,主要的原因是恢复程序得知道异常是在哪里抛出来的,并且得针对抛出地点做处理。这使得程序很难写同时也很难维护,因为大型程序有很多异常抛出点。