7.异常处理(Thinking in java学习七)

编译器并不能找出所有的错误,那么剩下的问题必须在运行期解决了。Java使用异常来提供一致的错误报告模型,使得构件
能够与客户端代码可靠地沟通问题。

概念

使用异常的好处:

  • 往往能够降低处理代码的复杂度;
  • 异常机制使代码的阅读、编写和调试工作更加井井有条。

基本异常

  • 异常情形是指阻止当前方法或作用域继续执行的问题。
  • 普通问题是指在当前环境下能得到足够的信息,总能处理这个错误。
  1. 异常参数

所有标准的异常类都有两个构造器:一个是默认构造器,一个是接受字符串作为参数,以便能把相关信息放入异常对象的构造器。

注意:抛出异常的方式能从当前的作用域退出。返回一个异常对象,然后退出方法或作用域。

  • 异常返回的地点与普通方法调用返回的地点完全不同。
  • 能够抛出任意类型的Throwable对象,它是异常类型的根类。

捕获异常

监控区域--它是一段可能产生异常的代码。

  1. try块

如果在方法内部抛出了异常,那么这个方法将在抛出异常的过程中结束。如果不希望该方法结束,则可以在方法内设置一个特殊的块来捕获异常。
完成任务的代码没有与错误的代码混在一起,意味着代码将更容易编写和阅读。

  1. 异常处理程序

抛出的异常必须在某处得到处理,这个地点就是异常处理程序。(catch)
当异常被抛出时,异常处理机制将负责搜寻参数与异常类型相匹配的第一个处理程序。

  1. 终止与恢复

异常处理理论上有两种模型,一种是终止模型,一种是恢复模型。

  • Java支持的终止模型,一旦异常被抛出,就表明错误已无法挽回,也不能回来继续执行了。
  • 恢复模型:异常处理程序的工作是修正错误,然后重新尝试调用出问题的方法,并认为第二次能成功。

恢复模型的缺点:恢复性的处理程序需要了解异常抛出的地点,这势必要包含依赖于抛出位置的非通用性代码。这增加了代码编写和维护的困难,对于异常可能会从很多地方抛出的大程序来说,更是如此。

创建自定义异常

class MyException extends Exception{
    public MyException (){ }
    public MyException (String msg){ super(msg); }
}

标准错误流e.printStackTrace();它将打印从方法调用处直到异常抛出处的方法调用序列。
e.printStackTrace(System.out);在控制台输出信息。

异常说明

Java提供相应的语法,使你能以礼貌的方式告知客户端程序员某个方法可能会抛出的异常类型,然后客户端程序员就可以进行相应的处理。
异常说明使用了附加的关键字throws,后面接所有潜在异常类型的列表。

捕获所有异常

捕获异常类型的基类Exception,就可以捕获所有类型的异常。

  1. 轨迹栈

printStackTrace()方法所提供的信息可以通过getStackTrace()方法来直接访问,这个方法将返回一个由栈轨迹中的元素构成的数组,其中每一分元素都表示栈中的一帧。

  1. 重新抛出异常

重新抛出异常会把异常抛给上一级环境中的异常处理程序,同一个try块的后续catch子句将被忽略。此外,异常对象的所以信息都得以保留,在高一级环境处理程序可以获得该异常的所有信息。

  1. 异常链

常常会在捕获一个异常后抛出另一个异常,并且希望把原始异常的信息保存下来,这个称为异常链。

现在所有的Throwable子类在构造器中都可以接受一个cause对象作为参数,这个cause对象就用来表示原始异常,这样通过原始异常传递给新的异常。

在Throwable子类中,只有三种基本的异常类提供了带cause参数的构造器,它们是

  • Error
  • Exception
  • RuntimeException。

注意:如果要把其他类型的异常链接起来,应该使用InitCase()方法而不是构造器。

Java标准异常

Throwable这个Java类被用来表示任何可以作为异常被抛出的类。Throwable对象可分为两种类型:

  • Error用来表示编译时和系统错误;
  • Exception是可以被抛出的基本类型,在Java类库、用户方法以及运行时故障中可能会抛出的Exception型异常。
  1. RuntimeException
    只能在代码中忽略RuntimeException类型的异常,其他类型异常的处理都是编译器强制实施的。究其原因,RuntimeException代表:
  • 无法预料的错误。
  • 作为程序员,应该在代码中进行检查的错误。

好处:给调试带来便利。

使用finally进行清理

finally子句无论try块中的异常是否抛出,它们都能得到执行。

  1. finally的作用

无论try块里发生什么,内存总能得到释放。

问题:Java在什么情况下使用finally呢?

当要把除内存之外的资源恢复到它们的初始状态时,就要用到finally。资源包括:已经打开的文件或网络连接,在屏幕上画的图形,甚至可以是外部世界的某个开关。
当涉及break及continue语句的时候,finally子句也会得到执行。

  1. 在return中使用finally

因为finally子句总是会执行的(return之前会先执行finally),所以在一个方法中,可以从多个点返回,并且保证重要的清理工作。

  1. 缺憾:异常丢失

Java的异常实现也有瑕疵,异常作为程序出错的标志,绝不应该被忽略,但它还是有可能被轻易地忽略。

  • finally中抛出的异常会取代前面的异常
try{
    try{
        throw new ExceptionOne();
    }finally{
        throw new ExceptionTwo();
    }
}catch(Exception e){
    e.println(e);
}

最后catch的异常是ExceptionTwo,而没有ExceptionOne.

  • 另一种异常丢失是从finally子句中返回
try{
    throw new ExceptionOne();
}finally{
    return;
}

即使抛出异常也不会有任何输出。

异常的限制

当覆盖方法时,只能抛出在基类方法的异常说明里列出的那些异常。这意味着当基类使用的代码应用到其派生类对象的时候,一样能够工作。

但异常限制对构造器不起作用,子类构造器可以抛出任何异常,且必须包含基类构造器的异常说明。子类的构造器不能捕获掉基类构造器抛出的异常。

构造器

问题:如果异常发生了,所有东西都能正确的清理吗?

如果构造器在其执行过程中半途而废,也许该对象的某些部分还没有被成功创建,而这些部分在finally子句中却是要被清理的。
对于在构造阶段可能会抛出异常,并且要求清理的类,最安全的使用方式使用嵌套的try子句。

异常匹配

抛出异常的时候,异常处理系统会按照代码的书写顺序找出“最近”的处理程序。找到匹配的处理程序之后,它就认为异常将得到处理,然后就不再继续查找了。

其他可选方式

异常处理系统就像一个活门,使你能放弃程序的正常执行序列。

异常处理的原则:只有在你知道如何处理的情况下才捕获异常

捕获异常的重要目标:就是把错误处理的代码用错误发生的地点相分离。

异常使用指南

  • 在恰当的级别处理问题。
  • 解决问题并且重新调用产生异常的方法。
  • 进行少许修补,然后绕过异常发生的地方继续执行。
  • 用别的数据进行计算,以代替方法预计会返回的值。
  • 把当前环境下能做的事情尽量做完,然后把相同的异常重抛到更高层。
  • 把当前环境下能做的事情尽量做完,然后把不同的异常抛到更高层。
  • 终止程序。
  • 进行简化。
  • 让类库和程序更安全。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Vinson武

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值