异常处理机制

Java为什么使用异常处理机制
  • 异常处理机制提供一致的错误报告模型,使得构件能够与客户端代码可靠地沟通问题。
  • 异常处理机制使代码的阅读、编写和调试工作更加井井有条。
  • 异常处理机制通过使用少于目前数量的代码来简化大型、可靠的程序的生成,并且通过这种方式使开发者更加自信:应用中没有未处理的错误。
Java标准异常
  • public class Throwable extends Object implements Serializable
  • Throwable被用来表示任何可以作为异常被抛出的类(异常类型的根类)。Throwable对象可分为两种类型:Error用来表示编译时和系统错误;Exception是可以被抛出的基本类型,在Java类库、用户方法以及运行时故障中都可能抛出Exception异常。
  • 特例:RuntimeException。
    • 运行时异常的类型有很多,这些异常都是从RuntimeException类继承而来的。它们会自动被Java虚拟机抛出,所以不必在异常说明中把它们列出来。它们也被称为“不受检查异常”,将被自动捕获。
    • 对于RuntimeException类型,编译器不需要异常说明,其输出被报告给了System.err。如果RuntimeException没有被捕获而直达main()(即如果运行前没有捕获到RuntimeException类型),那么在程序退出前将调用异常的printStackTrace()方法(即在运行中捕获到RuntimeException类型,异常情况会在程序退出前输出给System.err)。
    • 只能在代码中忽略RuntimeException(及其子类)类型的异常,其他类型异常的处理都是编译器强制实施的。
  • 所有标准异常类都有两个构造器:一个是默认构造器;另一个接受字符串作为参数,以便能把相关信息放入异常对象的构造器。
    Javab标准异常
基本异常
  • 异常情形(exceptional condition)是指阻止当前方法或作用域继续执行的问题。
  • Java处理抛出异常的步骤( 例如: throw new NullPointerException(“pointer=null”); ):
    • 使用new在堆上创建异常对象
    • 当前执行路径被终止,并且从当前环境中弹出对异常对象的引用传给关键字throw
    • 异常处理机制接管程序,并在异常处理程序中继续执行程序
      • 异常处理将会返回一个异常对象,然后退出当前方法或作用域,并执行异常处理程序。
      • 异常处理程序的任务是将程序从错误状态中恢复,以使程序能要么换一种方式运行,要么继续运行下去。
  • 异常使得我们可以将每件事情都当作一个事务来考虑。
    • 可以将异常看作是一种内建的恢复(undo)系统,因为在程序中可以拥有各种不同的恢复点。如果程序的某部分失败了,异常将“恢复”到程序中某个已知的稳定点上。
    • 异常允许(如果没有其他手段)强制程序停止运行,并抛出问题或者(理想状态下)强制程序处理问题,返回到稳定状态。
捕获异常
  • 如果方法内部抛出了异常(或者在方法内部调用的其他方法抛出了异常),这个方法将在抛出异常的过程中结束。要是不希望方法就此结束,可以在方法内设置一个特殊的块(try块)来捕获异常。
  • try块:跟在try关键字之后的普通程序块。把所有动作都放在try块里,然后只需在一个地方就可以捕获所有异常。——这意味着代码将更容易编写和阅读,因为完成任务的代码没有与错误检查的代码混在一起。
  • 异常处理程序:紧跟在try块之后,以关键字catch表示。针对每个要捕获的异常,得准备相应的处理程序。
    • 当异常被抛出时,异常处理机制将负责搜寻参数与异常类型相匹配的第一个处理程序。
    • 然后进入catch子句执行,此时认为异常得到了处理。
    • 一旦catch子句结束,则处理程序的查找过程结束。
  • 监控区域(guarded region):一段可能产生异常的代码,并且后面跟着处理这些异常的代码。
	try{
		// Code that might generate exceptions
	} catch(Type1 id1) {
		// Handle exceptions of Type1
	} catch(Type2 id2) {
		// Handle exceptions of Type2
	} catch(Type3 id3) {
		// Handle exceptions of Type3
	}
  • *异常处理理论上有两种基本模型:终止模型(Java支持)和恢复模型。
    • 终止模型:假设错误非常关键,以至于程序无法返回到异常发生的地方继续执行。一旦异常被抛出,就表明错误已无法挽回,也不能回来继续执行。
    • 恢复模型:异常处理程序的工作是修正错误,然后重新尝试调用出问题的方法,并认为第二次能成功。(导致耦合)
捕获所有异常
  • 通过捕获异常类型的基类Exception,就可以捕获所有类型的异常。
    • 最好把它放在处理程序列表的末尾,以防它抢在其他处理程序之前先把异常捕获了。
catch(Exception e) {
	System.out.println("Caught exceptions of any type");
}
  • 因为Exception是与编程有关的所有异常类的基类,所以不会包含太具体的信息,通过调取从其基类Throwable继承的方法来获取详细信息。
继承的方法主要的作用
String getMessage()获取详细信息
String getLocalizedMessage()获取用本地语言表示的详细信息
String toString()返回对Throwable的简单描述以及包含的详细信息
void printStackTrace()
void printStackTrace(PrintStream)
void printStackTrace(java.io.PrintWriter)
打印Throwable和Throwable的调用栈轨迹
Throwable fillInStackTrace()在Throwable对象的内部记录栈帧的当前状态,这在程序重新抛出错误或异常时很有用
  • 栈轨迹:printStackTrace()方法所提供的信息可以通过getStackTrace()方法来直接访问,这个方法将返回一个由栈轨迹中的元素所构成的数组,其中每一个元素都表示栈中的一帧。栈顶元素是调用序列中的最后一个方法调用(这个Throwable被创建和抛出之处)
  • 重新抛出异常:重抛异常会把异常抛给上一级环境中的异常处理程序,同一个try块的后续catch子句将被忽略(理解:实质上是将当前异常对象的引用重新抛出)。如果只是把当前异常对象重抛,那么 printStackTrace() 方法显示的将是原来异常抛出点的调用栈信息,而并非重新抛出点的信息。调用 fillInStackTrace() 方法,将更新为重新抛出点的信息, fillInStackTrace() 返回了一个Throwable对象,是通过把当前调用栈信息填入原来那个异常对象而建立的。
  • 异常链:在捕获一个异常后抛出另一个异常,并且希望把原始异常的信息保存下来。Throwable 的子类可以在构造器中接受一个 cause (因由)对象作为参数。cause 用来表示原始异常,使得即使在当前位置创建并抛出了新的异常,也能通过这个异常链追踪到异常最初发生的位置。对于没有能以 cause 作为参数的构造器的异常类型,可以使用 initCause() 方法。
异常说明
  • 异常说明即Java提供了相应的语法(并强制使用这个语法)来告知客户端程序员:某个方法可能会抛出的异常类型,然后客户端程序员就可以进行相应的处理。
  • 异常说明属于方法声明的一部分,紧跟在形式参数列表之后。
  • 异常说明使用了附加的关键字throws,后面接一个所有潜在异常类型的列表。
  • 异常说明机制是自顶向下强制执行的。
    • 如果方法里的代码产生了异常却没有处理,编译器强制要求要么处理这个异常,要么就在异常说明中表明此方法将产生异常。这样Java在编译时就可以保证一定水平的异常正确性。
  • 可以声明方法将抛出异常,实际上却不抛出。编译器会强制此方法的用户像真的抛出异常那样使用这个方法。
    • 这样可以为异常先占个位置,以后就可以抛出这种异常而不用修改已有的代码。在定义抽象基类和接口时这样做,派生类和接口实现就能够抛出这些预先声明的异常。
  • 在编译期被强制检查的异常称为被检查的异常
异常限制
  • 当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常或那些异常的派生异常,或者不抛出任何异常。这使得基类使用的代码应用到其派生类对象的时候依然能够工作(面向对象的基本概念)。
  • 异常限制对构造器不起作用,构造器可以增加新的异常说明或抛出新的异常,然而,因为基类构造器必须被调用,所以派生类构造器的异常说明必须包含基类构造器的异常说明。
  • 一个出现在基类方法的异常说明中的异常,不一定会出现在派生类方法的异常说明里。在继承和覆盖的过程中,某个特定方法的 “异常说明的接口” 不是变大了而是变小了——这恰好和类接口在继承时的情形相反。
异常匹配
  • 抛出异常后,异常处理系统会按照代码的书写顺序找出“最近”的处理程序。找到匹配的处理程序之后,就认为异常将得到处理,然后就不在继续查找。
  • 在书写顺序上,基类异常不能够写在其派生的异常前面,否则派生的异常将全部被“屏蔽”掉,编译器会报错。
异常使用
  • 在恰当的级别处理问题。(在知道该如何处理的情况下才捕获异常。)
  • 解决问题并且重新调用产生异常的方法。
  • 进行少许修补,然后绕过异常发生的地方继续执行。
  • 用别的数据进行计算,以代替方法预计会返回的值。
  • 把当前运行环境下能做的事情尽量做完,然后把相同的异常重抛到更高层。
  • 把当前运行环境下能做的事情尽量做完,然后把不同的异常重抛到更高层。
  • 终止程序。
  • 进行简化。
  • 让类库和程序更安全。(这既是在为调试做短期投资,也是为程序的健壮性做长期投资。)
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值