Java中异常处理技术

Java异常处理技术

1、我们知道如果程序在执行过程中,出现了异常,那么程序往往会在窗口处输出只有程序员才能看懂的错误解释语句,之后自动崩溃,但是这对于用户来说是莫名其妙的;

更加令人难以接受的是,在窗口应用程序当中如果出现异常的话,那么程序就会自动崩溃,不会有任何提示

2、在java中为了能够提供给用户一种优雅的处理方式,使窗口应用程序更加流畅,就要使用异常处理技术

3、除了2中的好处外,使用异常处理技术,可以将代码的主要实现功能的部分和处理异常的部分分开,那么就使得在主要的实现功能的代码处,

不会因为有过多的异常处理代码的分支语句而显得凌乱,也非常利于代码的维护;除此之外,我们有可能根本预计不到程序会出现何种异常,

使用异常处理中的(Exception)通用异常捕获对象的话,就能够解决这个问题

4、异常的产生:

在程序的执行过程中,如果某处出现了异常,那么程序会自动的生成一个相应种类的异常对象,在java程序中,异常是由对象来表示的,

通过在堆中,开辟异常对象,通过该异常对象(的引用)来将异常的相关信息传递(抛出)给处理异常的代码块;

由于出现异常时,程序会自动的生成这个下相应的对象,则不需要程序员显示的声明异常对象,因为往往程序员也不知道异常出现在哪里;

但是异常对象确实是可以进行显示的声明的:

   方式一:Exception e = new Exception();

           throw e ;

   方式二:throw new Exception();

           将异常声明和抛出同时进行

5、当然正如之前所说的那样,异常可分为不同的种类,java中已经为不同种类的异常定义了对应的异常类;我们可以在使用时查阅官方文档,

下面列出异常类的继承关系

 

如在java.lang、java.io、等等的包中都有该包对用的异常类,查阅起来也是很方便的;

我们打开官方文档就会发现在每个异常类的页面中都会显示该异常类的向上继承关系,而且还给出了给异常类的所有的子类;

在Exception异常类中的描述中有这样一段话
The class Exception and any subclasses that are not also subclasses of RuntimeException are checked exceptions

意思是:在那些继承自Exception的所有的直接子类中,除了同时那些还同时继承了RuntimeException的子类,都是受检类型的;

而继承自RuntimeException的子类都是非受检类型的;其中Error也是非受检类型的;

在最后的时候我们会讲解受检类型和非受检类型的区别

6、try-catch结构

形式:

try{

………………可能发生异常的代码块

}catch(异常类名 e1){

………………

}

catch(异常类 e2){

……………

}

①当在try块中,抛出异常的话,就在下面的catch中进行相应的异常类名的匹配,当匹配成功时,

就进入相应的catch块中,执行异常处理工作,一但抛出的异常对象被处理了,那么接下来的所有的catch块就都不会在执行

②在JSE7中引入了新的语法

catch(异常类名 e1 | 异常类名 e2 | 异常类名e3 | ………),

这样在该catch代码块中就用能够接受所有的在括号内部的异常类型的对象了

③catch块的先后顺序要注意,应该将接收子类异常的catch块放于接收父类异常对象的catch前面,

因为子类异常对象更加特殊,否则,如果将父类的放在前面的话,接收子类异常对象的catch快就永远不能被执行,

这种情况也是不允许的,如果这样写的话,会报错;此外,如果在try块中不可能抛出catch指定的异常接收对象话,那么编译器也会报错

④在为程序编写异常处理的catch块时,要想如下这样进行考虑:

   针对可能出现异常的try代码块,进行功能上的总合分析,设想可能出现哪些异常情况:比如可能出现内存越界的情况,

URL格式输入不正确的情况,等,那么就在catch快中,设置接收(ArrayIndexOutOfBoundsException)、(MalformedURLException)的catch快,

由于还不能确定程序当中知否还有其他的可能的异常,那么再将(Exception)这个通用的异常类加到最后的一个catch快中

7、finally语句

①finally块一般用在所有的catch块的最后,finally语句快一般总被执行,不管当前的异常是否被catch块接收;

但是要注意的是,finally语句块本身并不是像catch块那样用来解决异常的,即如果在执行当前的finally块之前,

异常还没有被某个catch块接收处理的话,即使执行了finally块,当前抛出的异常还是存在的,该异常对象还要向上一级进行传递,

继续寻找能够匹配的catch块

②finally块总是被执行,即使遇到return语句,finally还是要在该return语句之前被执行,之后在执行return语句;

但是如果遇到System.exit(0);则程序就不会执行对应的finally块了

③从上面的分析可以看到,其实finally的实质作用并不是用来进行异常处理的,其背后的原理是:

释放资源和清理代码中可能已经创建的任何乱七八糟的东西,以及即使保存需要保存的东西,避免因为异常而使得要保存的东西被损坏

也就是说,不管程序出没出现异常,在当前的代码块中或try代码块中,如果使用了很多需要关闭的资源,如文件资源,

套接字等或很多需要释放的乱七八糟的东西,这时就可以在finally块中将这些资源关闭

④finally块并不一定和catch并排使用,他也可以单独的和try块进行结合使用,如:

try{

    try{

       …………………………

}finally{……释放资源…… }

}catch(Exception e){…………}

⑤如果在finally块中又出现了异常的话(这太悲剧了);这是在相应的try块中产生的异常通常就已经丢失了

这个例子非常的好,当在内部的try块中出现类异常的话,finally块只用于将try块中的资源进行关闭,而抛出的异常,

抛给外部try块的catch块来处理;这样一来,就将资源释放和异常处理分开解决了,使程序更加的易于理解,分工就更加的明确

 

8、有一点需要注意的是----当抛出异常后程序的执行流程

①当程序的某处出现异常,就在程序就在该处自动生成对应的异常对象,并抛出;在当前域中的异常产生处之下的代码将不会被执行了

②抛出的异常会按照从当前级到上级的顺序逐一执行异常对象的匹配,知道找到匹配的catch块,将异常解决

③当异常在某个级别的catch中被捕获解决后,程序将在当前级的当前的try—catch结构后的代码处继续向下执行,

如果结构后面没有代码了,则将跳出程序,程序终止;而不会在回到异常抛出处继续执行了

(除非在捕获异常的catch块中,又继续调用了程序中的代码,使程序有能够再继续正常的执行,如:当输入的网址字符串不正确时,

catch块中要求重复的继续输入,之后将正确的网址字符串传给了某给方法,使得程序又能够正常执行)

9、在JavaSE7中已经给出了带资源的try语句用来完成和finally相似的功能,即在try关键字后面带上参数,如:

try(表带式){

…………………}catch{…………}

只是增加了一个表达式参数,这个表达式是一个申请资源的相关语句如:t

ry(InputSTream fis = new FileInputStream (source)){……………}

那么在try块中使用完这个文件流时,就不用想着在使用finally块来将文件流关闭了,程序会自动的将这个文件流关闭,

确保文件不被损坏,这样就提供了更加简单的方法来清理资源了

②同样可以在try参数中分配多个资源,用分号隔开,唯一需要限制的是:每一个这样的资源都必须实现java.AutoCloseable接口

10、Java中内置的异常类型有很多,可以通过查阅官方文档来进行熟悉,还有一个非常好的方法是能够根据控制台准确的看到程序出现了哪种异常:

①首先在程序当中相应的地方先不要进行异常的处理,因为往往我们也不知道将会出现哪种异常,我们先让程序运行起来,

如果程序出现了异常的话,在控制台上就会出现错误提示,提示的内容就是程序出现的异常的信息,这些信息包括异常的类型、

异常产生的位置、异常对象的传递路径;这样一来我们就能够知道到底出现了何种异常

②但是这种方法也不一定好到哪里去,因为我们并不知道程序会呈现那些异常,也许在自己运行程序时并没有出现异常提示,

但是当交给客户时就出现了异常额情况是非常常见的

所以最好是查阅官方文档,并按照6 ④的步骤编写异常处理代码

11、在Java中提供了throws关键字,用法如下:

 public void

Funtion()throws IOException,NumberFormatException

{…………………}

即这个关键字用在方法的声明后面,意思是:当该方法出现了异常的话,在当前方法中不做异常的处理,

而是直接将该异常对象直接抛向调用该方法的上一级的方法来处理,当然上一级的方法还可以继续使用throws将异常继续向上直接抛出

这样的好处在于,可以将给子方法的异常全部抛给上级的方法来统一处理

12、受检类型和非受检类型

①我们之前介绍异常时,说过,如果程序出现了异常但没有得到处理的话,那么程序就会中断执行,

这类异常其实是运行时的异常即RuntimeException,也成为非受检异常;但是还有一种异常,称为受检异常;

受检异常和非受检异常是不同的,非受检异常在程序的编译阶段是不会进行检查的,也就是说其实程序存在非受检异常的隐患,

程序还是会执行的,只有在执行的过程中才会发现异常并中断;但是受检异常不一样,这类异常在程序的编译阶段就会进行检查,

如果编译阶段发现某段代码中存在受检异常,那么就会出现变异错误,那么程序就根本不能够执行;

②在本文的开始时的继承图中曾经说过什么是受检的什么是非受检类型的;当这两类异常出现时,程序都会自动生成对应的异常对象,并将之抛出

③那么如何解决受检和非受检异常呢?其实用前面都是用前面讲的方法try_catch机构和throws语句;

④但是有一点非常重要:

实际上对非受检异常,程序对于这种异常处理,是没有要求必须进行异常处理的,因为程序是能够运行起来的,

只是我们为了能够让客户有流畅的体验,而进行处理的(这在实际中往往也都这样做);但是对于受检型的异常来说,

程序是要求必须进行异常处理,不论是用catch还是用throws抛向上一级的catch;

否则编译会出现错误,程序根本无法运行

⑤下面的例子非常明显:

    

异常主要出现在后面两个方法中,其中的IOException和FileNotFoundException异常都是受检的;

而NumberFormatException异常是非受检的,如果我们将后面两个方法的throws中的IOException和FileNotFoundException去掉的话,

那么程序就会出现编译错误,但是如果将NumberFormatException 去掉的话程序不会出现编译错误,

能够执行,但是如果这的出现了该类错误的话,也还是会抛出该类型的异常对象的,这就不用多说了

posted @ 2014-08-12 21:11  RoperLee  阅读(2368)  评论(0)    收藏  举报