去ATM机取钱,插入银行卡,没反应,这就是异常,可能是机器坏了,也可能是卡消磁了等等;读卡成功,输入密码时,铵错按钮,这也是异常;密码正确,想取¥1000,结果余额不足,这又是异常;钱取完了,卡被吞了,这还是异常;……
拿人来说,沙尘迷眼了,这是异常情况;喝水呛着了,这也是异常;水指不小心划破流血了,这也是异常;……
出现这些情况该怎么办?那就需要"异常机制",对于ATM机,他有自己的一套处理机制,来处理这些异常,比如进行相应的提示,以便取款者知道发生了什么情况好做相应的处理。而人,也有与生俱来的机能处理这些异常,像呛着了要不停的咳嗽,迷眼了会被刺激要流眼泪等等。
对于高大上的编程语言Java,自然也有自己的处理机制,该机制提供了程序退出的安全通道。当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器。上一篇博客《风雨java路之【基础篇】——异常处理今与昔》,从面向过程到面向对象,简要介绍了异常处理的“进化”。这篇博客将详细的介绍一下Java的异常处理机制。
虽然java中有异常处理机制,但是要明确一点,决不应该用"正常"的态度来看待异常。绝对一点说异常就是某种意义上的错误,就是问题,它可能会导致程序失败。之所以java要提出异常处理机制,就是要告诉开发人员,你的程序出现了不正常的情况,请注意。就像人出现咳嗽、流鼻血等等,说明有病,得治。
看到这样一个笑话:世界上最真情的相依,是你在try我在catch。无论你发神马脾气,我都默默承受,静静处理。 大多数新手对java异常的感觉就是:try...catch...。没错,这是用的最多的,也是最实用的。我的感觉就是:java异常是从"try...catch..."走来。
下面就来看看java的体系结构。
① 异常的继承结构:基类为Throwable,Error和Exception继承Throwable,RuntimeException和IOException等继承Exception,具体的RuntimeException继承RuntimeException。
② Error和RuntimeException及其子类称为未检查异常(unchecked),其它异常成为已检查异常(checked)。
2. 每个类型的异常的特点
Error体系 :
Error类体系描述了Java运行系统中的内部错误以及资源耗尽的情形。应用程序不应该抛出这种类型的对象(一般是由虚拟机抛出)。如果出现这种错误,除了尽力使程序安全退出外,在其他方面是无能为力的。所以,在进行程序设计时,应该更关注Exception体系。
Exception体系包括RuntimeException体系和其他非RuntimeException的体系 :
① 运行时异常RuntimeException:RuntimeException体系包括错误的类型转换、数组越界访问和除数为0等等。处理RuntimeException的原则是:如果出现RuntimeException,那么一定是程序员的错误。例如,可以通过检查数组下标和数组边界来避免数组越界访问异常。
②其他非RuntimeException(IOException等等):这类异常一般是外部错误,例如试图从文件尾后读取数据等,这并不是程序本身的错误,而是在应用环境中出现的外部错误。
③两者的区别
运行时异常表示无法让程序恢复运行的异常,导致这种异常的原因通常是由于执行了错误的操作。一旦出现错误,建议让程序终止。
受检查异常表示程序可以处理的异常。如果抛出异常的方法本身不处理或者不能处理它,那么方法的调用者就必须去处理该异常,否则调用会出错,连编译也无法通过。当然,这两种异常都是可以通过程序来捕获并处理的。
1. 5个关键字
Java异常处理通过5个关键字try、catch、throw、throws、finally进行管理。基本过程是用try语句块包住要监视的语句,如果在try语句块内出现异常,则异常会被抛出,你的代码在catch语句块中可以捕获到这个异常并做处理;还有以部分系统生成的异常在Java运行时自动抛出。你也可以通过throws关键字在方法上声明该方法要抛出异常,然后在方法内部通过throw抛出异常对象。finally语句块会在方法执行return之前执行。
2. 两种处理办法:
①在方法中用try...catch语句捕获并处理异常,catch语句可以有多个,用来匹配多个异常。例如:
public void p(int x){ try{ ... }catch(Exception e){ ... }finally{ ... } }②对于处理不了的异常或者要转型的异常,在方法的声明处通过throws语句抛出异常。例如:
public void test1() throws MyException{ ... if(....){ throw new MyException(); } }
四、定义和使用
1. 使用已有的异常类,假如为IOException、SQLException。try{ 程序代码 }catch(IOException ioe){ 程序代码 }catch(SQLException sqle){ 程序代码 }finally{ 程序代码 }2. 自定义异常类
创建Exception或者RuntimeException的子类即可得到一个自定义的异常类。例如:
public class MyException extends Exception{ public MyException(){} public MyException(String smg){ super(smg); } }
3. 使用自定义的异常
用throws声明方法可能抛出自定义的异常,并用throw语句在适当的地方抛出自定义的异常。例如:
在某种条件抛出异常
public void test1() throws MyException{ ... if(....){ throw new MyException(); } }
4.异常转型
将异常转型(也叫转译,实际上就是捕获到异常后,将异常以新的类型的异常再抛出,这样做一般为了异常的信息更直观),使得异常更易读易于理解
public void test2() throws MyException{ ... try{ ... }catch(SQLException e){ ... throw new MyException(); } }还有一个代码,很有意思:
public void test2() throws MyException{ ... try { ... } catch (MyException e) { throw e; } }
这段代码实际上捕获了异常,然后又和盘托出,没有一点意义,如果这样还有什么好处理的,不处理就行了,直接在方法前用throws声明抛出不就得了。异常的捕获就要做一些有意义的处理。
1. 能处理就早处理,抛出不去还不能处理的就想法消化掉或者转换为RuntimeException处理。因为对于一个应用系统来说,抛出大量异常是有问题的,应该从程序开发角度尽可能的控制异常发生的可能。
2. 对于检查异常,如果不能行之有效的处理,还不如转换为RuntimeException抛出。这样也让上层的代码有选择的余地――可处理也可不处理。
3. 对于一个应用系统来说,应该有自己的一套异常处理框架,这样当异常发生时,也能得到统一的处理风格,将优雅的异常信息反馈给用户。
最后来一句比较官方的话:不要为了使用异常而使用异常,有病乱投医。异常是程序设计的一部分,对它的设计也要考究点。