------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! ------
Java异常机制可以使程序中的异常处理代码和正常业务代码分离,保证程序代码更加优雅,并可以提高程序的健壮性。当程序运行中出现意外情况时,系统会自动生成一个Exception对象来通知程序,从而实现将“业务功能实现代码”和“错误处理代码”分离,提供更好的可读性。
Java异常机制主要依赖于try、catch、finally、throw和throws五个关键字,并且将异常分为两种,编译(Checked)异常和Runtime异常,编译异常是编译阶段被处理的异常,Runtime异常发生则无须处理,直接结束程序。
1、异常处理机制
1.1 使用try…catch捕获异常
Java提供了一种假设:如果程序可以顺利完成,那就“一切正常”,把系统的业务实现代码放在try块中定义,所有的异常处理逻辑放在catch块中进行处理。下面是具体的语法结构:
try{ //业务实现代码 ...... } catch (Exception e){ alert 输入不合法 goto retry }
如果执行try块里的业务逻辑代码是出现异常,系统自动生成一个异常对象,该对象被提交到java运行时环境,这个过程称为抛出(throw)异常。当java收到该对象,会寻找该异常对象的catch块,如果找到合适catch块,则把该异常交给该catch块处理,这个过程称为捕获(catch)异常。如果java找不到catch块则程序终止退出。
当java收到异常对象是,该怎么该异常对象寻找catch块呢?具体格式如下:
try{ //业务实现代码 ...... } catch (ClassException1 e1){ ...... } catch (ClassException2 e2){ ....... } ......
当java接受到异常对象时,会依次判断该异常对象是否是catch中异常类或其子类实例,如果是则调用该catch块,否则再次拿该异常对象和下一个catch块里的异常类进行对比。当程序进入负责处理的catch块中,会根据传入的异常类获取异常信息。
注意:try块与if不一样,try后面的{}不可以省略,catch也一样,还有try块声明的变量是局部变量,它只在try中有效,在catch中不能访问。
1.2 使用finally回收资源
有些是时候程序在try中打开了一些物理资源(如数据库连接、网络连接和磁盘文件等),这些物理资源都必须显式回收。为了保证一定能回收try块中打开的物理资源,java提供了finally,不论代码是否出现异常,甚至try块或catch块中执行了return语句,finally块总会被执行。具体语法格式如下:
//catch块也可省略 try{ //业务实现代码 ...... } catch (ClassException1 e1){ ...... } catch (ClassException2 e2){ ....... } ...... finally{ //资源回收块 ...... }
注意:除非在try和catch块中调用了退出虚拟机操作,否则finally块不管什么情况都会执行其中代码。尽量避免在finally块中使用return或throw等导致方法终止的语句,否则容易出现一些很奇怪的情况。
2、Checked异常和Runtime异常
Java的异常分为两大类:Checked和Runtime异常。所有RuntimeException类及其子类的实例被称为Runtime异常,其他的则被称为Checked异常。如果程序没有显式处理Checked异常,那么该程序无法编译通过。Runtime异常则无须显式声明抛出,如果程序需捕获Runtime异常,可用try…catch块来实现。
2.1 使用throws声明抛出异常
当当前方法不知道如何处理这种类型的异常,则可使用throws声明抛出异常,交给调用者处理,如果处理不了则可在用throws抛出,最终也可以交给虚拟机处理,虚拟机处理方法是打印异常信息,并终止程序。
Throws声明抛出只能在方法签名中使用,throws可以声明抛出多个异常类,多个异常类之间用逗号隔开。具体语法格式如下:
class ThrowsTest throws Exception1,Exception2......{}
如果某段代码使用了throws声明的方法,该方法声明抛出了Checked异常,则表明该方法希望他的调用者来处理该异常。
注意:大部分情况下推荐使用Runtime异常,因为更加简洁。
2.2 使用throw抛出异常
当程序出错时,java允许程序使用throw自行抛出异常。例如饭后出去散步,突然下雨,打破了原定计划,就属于一种异常。使用throw抛出异常,每次只能抛出一个异常实例,具体格式如下:
throw ExceptionInstance;
下面是一段简单的代码:
class ThrowDemo { static void throwOne() { System.out.println("throwOne."); //throw在内部抛出异常 throw new IllegalAccessException("demo"); } public static void main(String args[]) { throwOne(); } }
上面程序在编译时无法通过,因为throw抛出了IllegalAccessException异常,该异常属于Checked异常,如果要抛出必须在方法外面用throws声明,即throwOne() throws IllegalAccessException ,那么调用该方法的main函数必须使用try…catch进行捕捉,但是如果该异常是Runtime异常或其子类的话,则可以不必抛出。
注意:如果throw在内部抛出异常,如果使用了try…catch进行了异常捕捉的话,也可不必用throws在外部声明抛出异常,也就是说如果抛出的异常得到了解决,就可以不使用throws。
2.3 自定义异常
通常情况下,程序在抛出异常时,应该选择合适的异常类,从而明确地描述该异常情况。因此,程序常常需要抛出自定义异常。
用户自定义异常都应继承Exception基类,如果希望自定义Runtime异常,则应该继承RuntimeException基类。定义异常时通常需要两个构造器:一个是无参数构造器;另一个是带一个字符串参数的构造器,这个字符串将作为该异常对象的描述信息,下面代码建立了一个自定义异常:
public class MyExceptiion extends Exception{ //无参数的构造器 public MyExceptiion(){} //带一个字符串参数的构造器 public MyExceptiion(String msg){ super(msg); } }
上面程序的msg属于就是该异常的详细描述信息。如果需要定义Runtime异常,只需将上面Exception基类改为RuntimeExcep基类即可。
3、异常处理规则
成功的异常处理应该满足下面4点:
1.使程序代码混乱最小化
2.捕获并保留诊断信息
3.通知合适的人员
4.采用合适的方式结束异常活动。
不要过度的使用异常。滥用的话会带来一些负面影响。主要表现在:把异常和普通错误混淆在一起,不再编写任何错误处理代码,而是简单的抛出异常来代替所有的错误处理;使用异常处理来代替流程控制。
不要使用庞大的try块。这样会难免在try块后紧跟大量的catch块才可以这对不同的异常提供不同的处理逻辑,这样反而增加了编程的复杂度。
不要忽略捕获到的异常。建议对异常采取适当的措施,比如:处理异常,重写抛出异常或在合适的层处理异常。
我的这篇博文主要介绍了java异常处理机制的相关知识,比较详细的介绍了try、catch、finally、throw和throws5个关键字的用法,并介绍了Checked和Runtime异常之间的区别,最后讲到了java的异常处理原则,希望这些能对大家有些帮助。