💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
## Java编程那些事儿83——异常处理语法2 陈跃峰 出自:[http://blog.csdn.net/mailbomb](http://blog.csdn.net/mailbomb) ### 10.3.3 捕获异常及异常处理 在整个异常处理机制中,异常在系统中进行传递,传递到程序员认为合适的位置,就捕获到该异常,然后进行逻辑处理,使得项目不会因为出现异常而崩溃。 为了捕获异常并对异常进行处理,使用的捕获异常以及处理的语法格式为: ~~~                    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语句块中可能抛出的异常,否则将出现语法错误。 ### 10.3.4 声明自定义异常类 如果JDK API中提供的已有的异常类无法满足实际的使用需要,则可以根据需要声明自定义的异常类来代表项目中指定类型的异常。 异常类在语法上要求必须直接或间接继承Exception,可以根据需要选择继承Exception或RuntimeException类,这样也设定了自定义异常类的类型。如果直接继承Exception,则属于必须被处理的异常,如果继承RuntimeException,则不强制必须被处理。当然,可以根据需要继承其它Exception的子类。 在编码规范上,一般将异常类的类名命名为XXXException,其中XXX用来代表该异常的作用。 示例代码如下: ~~~ /** 自定义异常类  */ public class MyException extends RuntimeException {} ~~~ 自定义异常类的用途,则完全由程序员进行规定,可以在出现该异常类型的条件时抛出该异常,则就可以代表该类型的异常了。 在实际的项目中,有些时候还需要设计专门的异常类体系,来代表各种项目中需要代表的异常情况。 ### 10.4 异常处理方式 前面介绍了异常处理机制的相关语法,但是当出现异常时,如何进行处理是语法无法解决的问题,下面就介绍一下异常处理的方式。 异常处理,顾名思义就是将出现的异常处理掉,但是根据异常出现的位置以及异常的类型不同,会出现很多的方式,依次介绍如下: 1、不处理 该处理方式就是只捕获异常不进行处理。不推荐使用该方式。 例如: ~~~                                      String s = “abc”;                                      try{                                                int n = Integer.parseInt(s);                                      }catch(Exception e){                                                                                     } ~~~ 对于这样的处理,该异常被忽略掉了,有可能会影响后续逻辑的执行执行。该种处理方式一般被初学者使用的比较多。 2、直接处理掉 如果具备处理异常的条件,则可以根据不同的异常情况将该异常处理掉,例如给出用户错误原因的提示等或根据逻辑上的需要修正对应的数值。 例如: ~~~          /**  * 异常处理示例  * 该程序的功能是将用户输出的命令行参数转换为数字并输出  */ public class ExceptionHandle1 {          public static void main(String[] args) {                    int n = 0;                    try{                             //将输入的第一个命令行参数转换为数字                             n = Integer.parseInt(args[0]);                             //输出转换后的结果                             System.out.println(n);                    }catch(ArrayIndexOutOfBoundsException e){                             System.out.println("请输入命令行参数!");                    }catch(NumberFormatException e){                             System.out.println("命令行参数不是数字字符串!");                    }          } } ~~~ 在执行该代码时,如果发生数组下标越界异常,则代表用户未输入命令行参数,则提示用户输入命令行参数,如果发生数字格式化异常,则代表用户输入的第一个命令行参数不是数字字符串,则给出用户对应的提示信息。 该示例中就是根据异常出现的原因提示用户进行正确的操作,这是一种常见的异常处理的方式。 3、重新抛出 在实际的项目,有些时候也需要将某个方法内部出现的所有异常都转换成项目中自定义的某个异常类,然后再重新抛出。 例如: ~~~                                      try{                                                //逻辑代码                                      }catch(Exception e){                                                throw new MyException();                                      } ~~~ 这样转换以后,比较容易发现异常出现的位置,也方便项目中逻辑上的处理。 4、在方法中声明 如果当前方法或构造方法中,或在当前方法或构造方法中调用的其它方法,会抛出某个异常,而在当前方法或构造方法中没有足够的信息对该异常进行处理,则可以将该异常进行传递,将该异常传递给调用当前方法的位置在进行处理。这就是异常在系统中传递的方式。 例如: ~~~          /**  * 异常处理示例2  * 演示在方法中重新声明异常  */ public class ExceptionHandle2 {          public void test(String s) throws NumberFormatException{                    int n = Integer.parseInt(s);                    System.out.println(n);          } } ~~~ 在test方法中,由于parseInt方法会抛出NumberFormatException异常,而该方法内部无法对此进行处理,所以则在test方法内部声明把NumberFormatException异常抛出,这样该异常就由调用test的方法再进行处理。这样异常就可以在方法之间进行传递了。 这里列举了异常处理中常见的一些处理方式,当然也可以根据需要,进行其它的处理。 ### 10.5 总结 异常处理是Java语言中增强程序健壮性的一种机制,Java语法为异常处理提供了一系列的语法格式,在实际的项目中,需要根据具体的情况对异常给出对应的处理,使得异常能够被正确的处理掉,从而保证项目的正常执行。