首页 > 代码库 > 风雨java路之【基础篇】——异常的那些事儿

风雨java路之【基础篇】——异常的那些事儿

        异常,说白了,就是不正常,就是由于种种原因产生了非正常的结果。生活中此现象比比皆是,举个简单的例子:
        去ATM机取钱,插入银行卡,没反应,这就是异常,可能是机器坏了,也可能是卡消磁了等等;读卡成功,输入密码时,铵错按钮,这也是异常;密码正确,想取¥1000,结果余额不足,这又是异常;钱取完了,卡被吞了,这还是异常;……
        拿人来说,沙尘迷眼了,这是异常情况;喝水呛着了,这也是异常;水指不小心划破流血了,这也是异常;……
        出现这些情况该怎么办?那就需要"异常机制",对于ATM机,他有自己的一套处理机制,来处理这些异常,比如进行相应的提示,以便取款者知道发生了什么情况好做相应的处理。而人,也有与生俱来的机能处理这些异常,像呛着了要不停的咳嗽,迷眼了会被刺激要流眼泪等等。

        对于高大上的编程语言Java,自然也有自己的处理机制,该机制提供了程序退出的安全通道。当出现错误后,程序执行的流程发生改变,程序的控制权转移到异常处理器。上一篇博客《风雨java路之【基础篇】——异常处理今与昔》,从面向过程到面向对象,简要介绍了异常处理的“进化”。这篇博客将详细的介绍一下Java的异常处理机制。


        一、友情提醒

        虽然java中有异常处理机制,但是要明确一点,决不应该用"正常"的态度来看待异常。绝对一点说异常就是某种意义上的错误,就是问题,它可能会导致程序失败。之所以java要提出异常处理机制,就是要告诉开发人员,你的程序出现了不正常的情况,请注意。就像人出现咳嗽、流鼻血等等,说明有病,得治。


        二、基本概念
        看到这样一个笑话:世界上最真情的相依,是你在try我在catch。无论你发神马脾气,我都默默承受,静静处理。 大多数新手对java异常的感觉就是:try...catch...。没错,这是用的最多的,也是最实用的。我的感觉就是:java异常是从"try...catch..."走来。

        下面就来看看java的体系结构。

异常体系结构

        1. 异常的分类:
        ① 异常的继承结构:基类为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. 对于一个应用系统来说,应该有自己的一套异常处理框架,这样当异常发生时,也能得到统一的处理风格,将优雅的异常信息反馈给用户。
        最后来一句比较官方的话:不要为了使用异常而使用异常,有病乱投医。异常是程序设计的一部分,对它的设计也要考究点。