在整个异常处理机制中,异常在系统中进行传递,传递到程序员认为合适的 位置,就捕获到该异常,然后进行逻辑处理,使得项目不会因为出现异常而崩溃。 为了捕获异常并对异常进行处理,使用的捕获异常以及处理的语法格式为: try{ //逻辑代码 }catch(异常类名 参数名){ //处理代码 } 在该语法中,将正常的程序逻辑代码书写在 try 语句块内部进行执行,这些 代码为可能抛出异常的代码,而 catch语句中书写对应的异常类的类名,在 catch 语句块内部书写出现该类型的异常时的处理代码。 程序执行到 try-catch 语句时,如果没有发生异常,则完整执行 try 语句块内 部的所有代码,而 catch 语句块内部的代码不会执行,如果在执行时发生异常, 则从发生异常的代码开始,后续的 try 语句块代码不会执行,而跳转到该类型的 异常对应的 catch 语句块中。 示例代码如下: String s = "123"; try{ int n = Integer.parseInt(s); System.out.println(n); }catch(NumberFormatException e){ System.out.println("该字符串无法转换! "); } 在 该 示 例 代 码 中 , Integer类 的 parseInt 方 法 可 能 会 抛 出 NumberFormatException,因为 parseInt 方法的声明如下: public static int parseInt(String s) throws NumberFormatException 这里字符串 s转换为 int没有发生异常,则程序执行完 try语句块内部的代码, 程序的运行结果为: 123 如果将字符串对象 s 的值修改为”abc”,则运行上面的代码,则 parseInt 方法 执行时将抛出 NumberFormatException,则调用 parseInt 方法语句后续的 try 语句 块中的代码不会执行,程序的执行流程跳转到捕获 NumberFormatException异常 的 catch 语句块内部,然后执行该 catch 语句块内部的代码,则程序的执行结果 是: 该字符串无法转换! 这就是最基本的捕获异常和异常处理的代码结构。使用 try 语句捕获程序执 行时抛出的异常,使用 catch 语句块匹配抛出的异常类型,在 catch 语句块内部 书写异常处理的代码。 在实际程序中,也可以根据异常类型的不同进行不同的处理,这样就需要多 个 catch 语句块,其结构如下: try{ //逻辑代码 } catch(异常类名 1 参数名 1){ //处理代码 1 } catch(异常类名 2 参数名 2){ //处理代码 2 } …… }catch(异常类名 n 参数名 n){ //处理代码 n } 例如: String s = "123"; try{ int n = Integer.parseInt(s); System.out.println(n); char c = s.charAt(4); System.out.println(c); }catch(NumberFormatException e){ System.out.println("该字符串无法转换! "); }catch(StringIndexOutOfBoundsException e){ System.out.println("字符串索引值越界"); } 在执行时,按照 catch 语句块书写的顺序从上向下进行匹配,直到匹配到合 适的异常就结束 try-catch 语句块的执行。 在实际执行时,就可以根据捕获的异常类型不同,书写不同的异常处理的代 码了。使用该语法时需要注意,如果这些异常类直接存在继承关系,则子类应该 书写在上面,父类应该书写在下面,否则将出现语法错误。例如: String s = "123"; try{ int n = Integer.parseInt(s); System.out.println(n); char c = s.charAt(4); System.out.println(c); }catch(Exception e){ }catch(NumberFormatException e){ //语法错误,异常已经被处理 System.out.println("该字符串无法转换! "); }catch(StringIndexOutOfBoundsException e){ //语法错误,异常已经被处 理 System.out.println("字符串索引值越界"); } 这里 Exception 类是所有异常类的父类,在匹配时可以匹配到所有的异常, 所有后续的两个异常处理的代码根本不会得到执行,所以将出现语法错误。正确 的代码应该为: String s = "123"; try{ int n = Integer.parseInt(s); System.out.println(n); char c = s.charAt(4); System.out.println(c); }catch(NumberFormatException e){ System.out.println("该字符串无法转换! "); }catch(StringIndexOutOfBoundsException e){ System.out.println("字符串索引值越界"); }catch(Exception e){ } 如果在程序执行时,所有的异常处理的代码都是一样的,则可以使用 Exception 代表所有的异常,而不需要一一进行区分,示例代码如下: String s = "123"; try{ int n = Integer.parseInt(s); System.out.println(n); char c = s.charAt(4); System.out.println(c); }catch(Exception e){ //统一的处理代码 } 在实际使用时,由于 try-catch的执行流程,使得无论是 try 语句块还是 catch 语句块都不一定会被完全执行,而有些处理代码则必须得到执行,例如文件的关 闭和网络连接的关闭等,这样如何在 try 语句块和 catch 语句块中都书写则显得 重复,而且容易出现问题,这样在异常处理的语法中专门设计了 finally 语句块来 进行代码的书写。语法保证 finally 语句块内部的代码肯定获得执行,即使在 try 或 catch 语句块中包含 return 语句也会获得执行,语法格式如下: finally{ //清理代码 } 该语法可以和上面的两种 try-catch语句块匹配,也可以和 try 语句匹配使用, 和 try 语句块匹配的语法格式如下: try{ //逻辑代码 }finally{ //清理代码 } 这样在执行时,无论 try 语句块中的语句是否发生异常, finally 语句块内部 的代码都会获得执行。 而只书写 finally 而不书写 catch 则会导致异常的丢失,所以最常用的异常处 理的语法格式还是如下语法: try{ //逻辑代码 }catch(异常类名 参数){ //异常处理代码 }finally{ //清理代码 } 这样就是整个异常处理部分的逻辑代码、异常处理代码和清理的代码成为一 个整体,使得程序代码更加显得紧凑,便于代码的阅读和使用。 最后,介绍一下使用异常处理语法时需要注意的问题: 1、书写在 try 语句块内部的代码执行效率比较低。所以在书写代码时,只把可能 出现异常的代码书写在 try 语句块内部。 2、如果逻辑代码中抛出的异常属于 RuntimeException 的子类,则不强制书写在 try 语句块的内部,如果抛出的异常属于非 RuntimeException 的子类,则必须进 行处理,其中书写在 try 语句块是一种常见的处理方式。 3、 catch语句块中只能捕获 try 语句块中可能抛出的异常,否则将出现语法错误。
声明自定义异常类 如果 JDK API中提供的已有的异常类无法满足实际的使用需要,则可以根据 需要声明自定义的异常类来代表项目中指定类型的异常。 异常类在语法上要求必须直接或间接继承 Exception,可以根据需要选择继承 Exception 或 RuntimeException 类,这样也设定了自定义异常类的类型。如果直 接继承 Exception,则属于必须被处理的异常,如果继承 RuntimeException,则不 强制必须被处理。当然,可以根据需要继承其它 Exception 的子类。 在编码规范上,一般将异常类的类名命名为 XXXException,其中 XXX 用来 代表该异常的作用。 示例代码如下: /** * 自定义异常类 */ public class MyException extends RuntimeException {} 自定义异常类的用途,则完全由程序员进行规定,可以在出现该异常类型的条件 时抛出该异常,则就可以代表该类型的异常了。 在实际的项目中,有些时候还需要设计专门的异常类体系,来代表各种项目中需 要代表的异常情况。
异常处理方式 前面介绍了异常处理机制的相关语法,但是当出现异常时,如何进行处理是
|