《java 编程思想系列之8》 通过异常处理错误

一、概念

二、基本异常

当抛出异常之后,有几件事情会随之发生:
首先,同java中其他对象的创建一样,将使用new在堆上穿件异常对象。然后当前的执行路径(它不能继续下去了)被终止,并且从当前环境中弹出对异常对象的引用。此时,异常处理机制接管程序,并开始寻找一个恰当的地方来继续执行程序。这个恰当的地方就是异常处理程序

1、异常参数

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

三、捕获异常

监控区域

1、try块
异常处理程序必须紧跟在try块之后。当异常被抛出时候,异常处理机制将负责搜寻参数与异常类型相匹配的第一个处理程序。然后进入catch子句执行,此时认为异常得到了处理。一旦catch子句结束,则处理程序的查找过程结束。

2、终止与恢复

异常处理理论上有两种基本类型。java支持终止模型(它是java和c++所支持的模型)。

四、创建自定义的异常

要子句定义异常类,必须从已有的异常类继承,最好是选择意思相近的异常类继承。

printStackTrace()方法:从方法调用处直到异常抛出处的方法调用序列。

1、异常与记录日志



静态的Logger.getLogger()方法创建一个String参数相关联的Logger对象(通常与错误相关的包名和类名),这个Logger对象会将其输出发送到System.err。向Logger写入的最简单方法就是直接调用与日志记录消息级别相关联的方法,这里使用的是severe().

五、异常说明

可以声明方法将抛出异常,实际上却不抛出
被检查的异常:在编译时候被强制检查的异常

六、捕获所有异常

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

如果只是把当前异常对象重新抛出,那么printStackTrace()方法显式的将是原来异常抛出点的调用栈信息,而并非重新抛出点的信息。要想更新这个信息,可以调用fillInStackTrace()方法,这将返回一个Throwable对象,它是通过把当前调用栈信息填入原来那个异常对象而建立的:



有可能在捕获异常之后抛出另一个异常。那么做的话,得到的效果类似于使用fillInStackTrace(),有关原来异常发生点的信息会丢失,剩下的是与新的抛出点有关的信息。

永远不必为清理前一个一场对象而担心,或者说为异常对象的清理而担心。它们都是用new在堆上创建的对象,所以垃圾回收器会自动把它们清理掉。

3、异常链

在Throwable的子类中,只有三种基本的异常提供了带cause参数的构造器,它们是Error(用于java虚拟机报告系统错误)、Exception以及RuntimeException。如果要把其他类型的异常链接起来,应该使用initCause()方法而不是构造器。

七、java标准异常

1、特例:RuntimeException

RuntimeException(或者任何从它继承的异常)是一个特例。对于这种异常类型,编译器不需要异常说明,并输出被报告给了System.err.

如果RuntimeException没有被捕获而直达main(),那么在程序退出前将调用异常的printStackTrace()方法。

只能在代码中忽略RuntimeException(及其子类)的异常,其他类型的异常的处理都是由编译器强制实施的。究其原因,RuntimeException代表的是编程错误:
1)无法预料的错误。比如从你控制范围之外传递进来的null引用
2)代码中进行检查的错误

八、使用finally进行清理

1、finally用来做什么

当要把除了内存之外的资源恢复到它们的初始状态的时候,就要用到finally子句。这种需要清理的资源包括:已经打开的文件或者网络连接,在屏幕上画的图形,甚至可以是外部世界的某个开关。

2、在return中使用finally


3、缺憾:异常丢失


九、异常的限制

当覆盖方法的时候,只能抛出在基类方法的异常说明里列出的那些异常。这个限制很有用,因为这意味着,当基类使用的代码应用到其他派生类对象的时候,一样能够工作,异常也不例外:
1)异常限制对构造器不起作用

你会发现StormyInning的构造器可以抛出任何异常,而不必理会基类构造器所抛出的异常。然后,因为基类构造器必须以这样或者那样的方式被调用,派生类构造器的异常说明必须包含基类构造器的异常说明。

2)StormyInning.walk()不能通过编译的原因:它抛出了异常,而Inning.walk()并没有声明此异常。

3)覆盖后的event()方法表明,派生类方法可以不抛出异常,即使它是基类所定义的异常。

4)在main方法中,如果处理的刚好是StormyInning对象的话,编译器只会强制要求你捕获这个类所抛出的异常。但是如果将它向上转型为基类型,那么编译器就会(正确的)要求你捕获基类的异常。

5)一个出现在基类方法中的异常说明中的异常,不一定会出现在派生类方法的异常说明里。这点同继承的规则明显不同,在继承中,基类方法必须出现在派生类里。换句话说,在继承和覆盖的过程中,某个特定的方法的“异常说明的接口”不是变大而是变小了----这恰好和类接口在继承时候的情形相反。

十、构造器

基本规则:在创建需要清理的对象之后,立即进入一个try-finally语句块。

对于在构造阶段可能会抛出异常,并且要求清理的类,最安全的使用方式就是使用嵌套的try子句。

十一、异常匹配

查找的时候并不要求抛出的异常同处理程序所声明的异常完全匹配,派生类的对象也没有匹配其基类的处理程序:


如果把捕获基类的catch子句放在最前面,以此想把派生类的异常全给“屏蔽”,就像这样:
这样编译器就会发现Sneeze的catch子句永远也得不到执行,因此它会向你报告错误。








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值