Java语言 异常与处理机制


  Java语言把程序运行中可能遇到的错误分为两类,一类是非致命性的,通过某种修正后程序还能继续执行。这类错误称为异常(Exception)。如打开一个文件时,发现文件不存在。又比如说除0溢出、数组越界等。这一类错误可以借助程序员的处理来恢复。
  另一类是致命性的,即程序遇到了非常严重的不正常状态,不能简单地恢复执行,这就是错误。比如程序运行过程中内存耗尽。异常处理要考虑的问题包括:如何处理异常?把异常交给谁去处理?程序又该如何从异常中恢复?
  
  1. 异常及处理机制:
    为了解决异常问题,Java提供了异常处理机制,预定义了一个Exception类。当程序中发生异常时,通常不是简单地结束程序,而是转去执行某段特殊代码来处理这个异常,设法恢复程序继续执行。但是如果程序遇到错误时,往往不能从中恢复,因此最好的办法是让程序中断执行。
    在一个方法的运行过程中,如果发生了异常,称程序产生了一个异常事件,相应地生成异常对象。该对象可能由正在运行的方法产生,也可能由JVM生成。这个对象中包含了该异常必要的详细信息,包括所发生的异常事件的类型及异常发生时程序的运行状态.
    生成的异常对象传递给Java运行时系统,运行时系统寻找相应的代码来处理这个异常. 生成异常对象并把它提交给运行时系统的这一过程称为抛出(Throw)一个异常.
    Java运行时系统从生成对象的代码块开始进行回溯,沿方法的调用栈逐层回溯,寻找相应的处理代码,直到找到包含相应异常处理的方法为止,并把异常对象交给该方法处理. 这一过程称为捕获(Catch). 当发现并响应相应异常时,就是处理(Handle)了异常。
    简而言之,发现错误的代码可以“抛出”一个异常,程序员可以“捕获”该异常,如果可能则“处理”它,然后恢复程序的执行。


2. 异常分类:
    Java语言在所有的预设包中都定义了异常类和错误类。Exception类是所有异常类的父类,Error类是所有错误类的父类,这两个类同时又是Throwable类的子类。虽然异常属于不同的类,但所有这些类都是标准类Throwable的后代。
    Throwable在Java类库中,不需要import语句就可以使用。异常分为三种:{ 1.受检异常,必须被处理。 2.运行时异常,不需要处理。 3.错误,不需要处理。}
      1.受检异常(Checked Exception)是程序执行期间发生的严重事件的后果。例如,如果程序从磁盘中读入数据,而系统找不到含有数据的文件,将会发生受检异常。这个异常所属类的类名是FileNotFoundException。发生错误的原因可能是文件丢失等等,写的好的程序通常都会预见这种问题并处理。
      Java类库中的所有类都是使用名字来描述异常原因的。这些类表示受检异常: ClassNotFoundException / FileNotFoundException / IOException / NoSuchMethodException 及 WriteAbortedException.
  
      2.运行时异常(Runtime Exception)通常是程序逻辑错误的结果。例如,数组下标越界导致ArrayIndexOutOfBounds异常。被0除导致ArithmeticException异常。虽然可以添加代码来处理运行时异常,但一般只需要修改程序中的错误即可。
      运行时异常的所有类都是类RuntimeException的子类,它是Exception的后代。
      Java类库中的这些类表示运行时异常: ArithmeticException /  ArrayIndexOutOfBoundsException / ClassCastException / EmptyStackException / IllegalArgumentException / IllegalStateException / indexOutofBoundsException / NoSuchElementException / NullPointerException 和 UnsupportedOperationException

      3.错误(Error)是标准类Error或其后代类的一个对象, Error是Throwable的后代。一般地,错误是指发生了不正确的情况,如内存溢出。这些情况都比较严重,一般程序很难处理。所以,即使处理错误是合法的,一般也不需要处理它们。
      运行时异常和错误称为不检异常(Unchecked Exception)。
    有些异常类在使用时必须要引入。例如,当在程序中使用IOException时,必须要使用import java.io.IOException;


3.处理异常:
    当发生异常时,程序通常会中断执行,并输出一条信息。对所发生的异常进行处理的就是异常处理。异常处理的重要性在于程序不但能发现异常,还要能捕获异常。对于可能引发受检异常的方法,有两种选择:在方法内处理异常,或时告诉方法的调用者来处理。
    比如,方法method1 调用 method2,method2又调用method3,进而method3又调用method4。在方法method4中如果出现异常,则在调用栈中的任何一个方法都可以捕获并处理这个异常。
    要处理异常,必须先标出可能引起异常的Java语句,还必须决定要找哪个异常。处理异常的代码含有两段。第一段try块含有可能抛出异常的语句。第二段含有一个或多个catch块。每个catch块含有捕获及处理某中类型异常的代码。
    catch(IOException e)标识符 e 称为catch块参数,它表示catch块将要处理IOException对象。参数e表示一个实际的异常。作为一个对象,每个异常都有存取方法getMessage(),它返回抛出异常时创建的描述字符串。通过显示这个字符串可知所发生异常的性质。
    catch块执行完毕,执行它后面的语句。但是如果问题很严重,则catch块可以调用exit方法来中止程序,如 System.exit(0); 赋给函数System.exit的参数0,表示虽然遇到了一个严重问题,但程序是正常结束的。
    单一一个try块中的语句,可能会抛出不同类型异常中的任意一个。在这样的try块后的catch块需要能捕获多个类的异常。为此,可以在try块后写多个catch块。当抛出一个异常时,为了能使所写的catch块真正捕获到相应的异常,catch块出现的次序很重要。程序的执行流程进入到其参数与异常类型相匹配的第一个catch块 ———— 按照出现的次序
    例如,下列的catch块次序不好,因为用于FileNotFoundException的catch块永远不会被执行:
    catch(IOException e) { .... } 
    catch(FileNotFoundException e) { .... }
    按照这个次序,任何I/O异常都被第一个catch块捕获。因为FileNotFoundException 派生于IOException,所以FileNotFoundException异常是IOException异常的一种,将与第一个catch块的参数相匹配。幸运的是,编译程序可能会对这样的次序给出警告信息。
    正确的次序是:
    catch(FileNotFoundException e) { .... } 
    catch(IOException e) { .... }  //处理所有其他的IOException
    因为受检异常和运行时异常的类都以Exception为祖先,故应避免在catch中使用Exception,而是尽可能地捕获具体的异常,且先捕获最具体的。
    try-catch的语法格式如下:
    try {
      //可能抛出异常的代码
    } catch (异常类型1 e) {
      //抛出异常类型1时要执行的代码,可能包含:
      System.out.println(e.getMessage());
    } catch (异常类型2 e) {
      //抛出异常类型2时要执行的代码,可能包含:
      System.out.println(e.getMessage());
    } finally {
      // 必须执行的代码
    }
    其中,异常类型1、异常类型2是产生的异常类型。根据发生异常所属的类,找到相应的catch语句,然后执行其中的语句。尽量避免在try块或catch块中再嵌套try-catch块;
    不论是否捕获异常,都要执行finally后的语句。一般地,为了统一处理程序出口,可将要处理的内容放到finally后的代码段中。
    try后的大括号中的代码称为保护代码。如果在保护代码内执行了System.exit()方法,将不在执行finally后面的语句,这是不执行finally后面语句的唯一一种可能。

 


4.公共异常:
    为了方便处理异常,Java预定义了一些常见的异常,下面列举几个常用到的异常。
    1.ArithmeticException
      整数除法中,如果除数为0,则发生异常,如这个表达式将引发ArithmeticException异常:int i = 12/0;
    2.NullPointerException
      如果一个对象还没有实例化,那么访问该对象或调用它的方法将导致NullPointerException异常。例如:
      image im [] = new image[4];
      System.out.println(im[0].toString());
      第一行创建了有4个元素的数组im,每个元素是image类型,系统为其进行初始化,每个元素的值为null,表明它还没有指向任何实例。第二行要访问im[0],由于访问的是还没有进行实例化的空引用,因此会导致NullPointerException。
    3.NegativeArraySizeException
      按常规,数组的元素个数应是一个大于等于0的整数。创建数组时,如果元素个数是负数,则会引发NegativeArraySizeException异常。
    4.ArrayIndexOutOfBoundsException
      Java把数组看作是对象,并用length变量记录数组的大小。访问数组元素时,运行时环境根据length值检查下标的大小。如果数组下标越界,则将导致ArrayIndexOutOfBoundsException异常。

 


5.抛出异常:
    Java要求如果一个方法确实引发了一个异常(当然Error或RuntimeException两类错误除外),那么在方法中必须写明相应的处理代码。
    处理异常有两种方法。一种是try块和catch块,捕获到所发生的异常类,并进行相应的处理。当然,catch块可以为空,表示对发生的异常不进行处理。
    另一种方法是,不在当前方法内处理异常,而是把异常抛出到调用方法中。当不能使用合理的方式来解决不正常或意外事件的情形下,才抛出异常。
    方法内执行throw语句时会抛出一个异常,一般形式如:throw exception_object;
    throw语句创建了一个异常对象 exception_object ,如: throw new IOException(); 这个语句创建类IOException的一个新对象并抛出它。抛出异常类型时也应该尽可能地具体。
      相应地,在说明方法时,要使用这个格式:    访问权限修饰符 返回值类型 方法名(参数列表) throws 异常列表
      紧接在关键字throws后面的是该方法内可能发生且不进行处理的所有异常列表。各异常之间用逗号分隔。例如:public void troubleSome() throws IOException
      一般地,如果一个方法引发了一个异常,而它自己又不处理,就要由调用者方法进行处理。如果方法内含有一个抛出异常的throw语句,则要在方法头添加一个throws子句,而不是在方法体内捕获异常。一般地,抛出异常及捕获异常应该在不同的方法内。
      在方法头中用Java保留字throws来声明这个方法可能抛出的异常;在方法体中用保留字throw实际抛出一个异常。  注意,这两个保留字不要混淆;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

明日复明日_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值