java异常处理机制

  

    简述:异常的英文单词是exception,字面翻译就是“意外、例外”的意思,也就是非正常情况。事实上,异常本质上是程序上的错误,包括程序逻辑错误和系统错误。比如使用空的引用、数组下标越界、内存溢出错误等,这些都是意外的情况,背离我们程序本身的意图。错误在我们编写程序的过程中会经常发生,包括编译期间和运行期间的错误,在编译期间出现的错误有编译器帮助我们一起修正,然而运行期间的错误便不是编译器力所能及了,并且运行期间的错误往往是难以预料的。假若程序在运行期间出现了错误,如果置之不理,程序便会终止或直接导致系统崩溃,显然这不是我们希望看到的结果。因此,在运行期间通过异常机制来处理程序运行期间出现的错误。通过异常机制,我们可以更好地提升程序的健壮性。

  •  Java异常类层次结构:

    在Java中异常被当做对象来处理,根类是java.lang.Throwable类,在Java中定义了很多异常类(如OutOfMemoryError、NullPointerException、IndexOutOfBoundsException等),这些异常类分为两大类:Error和Exception。

  图:

  blob.png

   

   Error是无法处理的异常,比如OutOfMemoryError,一般发生这种异常,JVM会选择终止程序。因此我们编写程序时不需要关心这类异常。

   

   Exception,也就是我们经常见到的一些异常情况,比如NullPointerException、IndexOutOfBoundsException,这些异常是我们可以处理的异常。

   

   Exception类的异常包括checked exception和unchecked exception。

        checked exception:(检查异常)非运行时异常java编译器强制程序员必须进行捕获处理,比如常见的IOExeption和SQLException。对于非运行时异常如果不进行捕获或者抛出声明处理,编译都不会通过。

       unchecked exception(非检查异常),也称运行时异常(RuntimeException),比如常见的NullPointerException、IndexOutOfBoundsException。对于运行时异常,java编译器不要求必须进行异常捕获处理或者抛出声明,由程序员自行决定。

  •  处理异常:

        在Java中如果需要处理异常,必须先对异常进行捕获,然后再对异常情况进行处理。如何对可能发生异常的代码进行异常捕获和处理呢?

    1.捕获异常:

        使用try和catch关键字即可。

        代码所示:

        1.try{           
            2.  //(尝试运行的)程序代码   
        3.}catch(异常类型 异常的变量名){   
            4.  //异常处理代码   
        5.}finally{   
            6.  //异常发生,方法返回之前,总是要执行的代码   
        7.}

 

        被try块包围的代码说明这段代码可能会发生异常,一旦发生异常,异常便会被catch捕获到,然后需要在catch块中进行异常处理。


     2.异常抛出:

        在Java中还提供了另一种异常处理方式即抛出异常,顾名思义,也就是说一旦发生异常,我把这个异常抛出去,让调用者去进行处理,自己不进行具体的处理,此时需要用到throw和throws关键字。     

public class Main {
    public static void main(String[] args) {
        try {
            createFile();
        } catch (Exception e) {
            // TODO: handle exception
        }
    }
     
    public static void createFile() throws IOException{
        File file = new File("d:/a.txt");
        if(!file.exists())
            file.createNewFile();
    }
}

       在实际的createFile方法中并没有捕获异常,而是用throws关键字声明抛出异常,即告知这个方法的调用者此方法可能会抛出IOException。那么在main方法中调用createFile方法的时候,采用try…catch块进行了异常捕获处理。

当然还可以采用throw关键字手动来抛出异常对象。

public class Main {
    public static void main(String[] args) {
        try {
            int[] data = new int[]{1,2,3};
            System.out.println(getDataByIndex(-1,data));
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
         
    }
     
    public static int getDataByIndex(int index,int[] data) {
        if(index<0||index>=data.length)
            throw new ArrayIndexOutOfBoundsException("数组下标越界");
        return data[index];
    }
}

  •   常见的异常处理方式: 

     1、对代码块用try..catch进行异常捕获处理;

     2、方法体外用throws进行抛出声明,告知方法调用者这代码会出现异常,需要处理:

         如果声明抛出的异常是非运行时异常,此方法的调用者必须显示地用try..catch块进行捕获或者继续向上层抛出异常。

         如果声明抛出的异常是运行时异常,此方法的调用者可以选择地进行异常捕获处理。

     3、用throw手动抛出一个异常对象

         如果声明抛出的异常是非运行时异常,此方法的调用者必须显示地用try..catch块进行捕获或者继续向上层抛出异常。

         如果声明抛出的异常是运行时异常,此方法的调用者可以选择地进行异常捕获处理。

  • try、catch、finally、throw、throws深入分析:

     try关键字用来包围可能会出现异常的逻辑代码,它单独无法使用,必须配合catch或者finally使用。Java编译器允许的组合使用形式只有以下三种形式:

     try…catch…;       try….finally……;    try….catch…finally…

    1. try、catch、finally语句块的执行顺序:

       当然如果没有发生异常,则catch块不会执行。但是finally块无论在什么情况下都是会执行的(这点要非常注意,因此部分情况下,都会将释放资源的操作放在finally块中进行),就算try代码块内使用return 关键字finally块也会执行,所以在编程中finally多使用在除内存之外的资源恢复到它们的初始化时,比如资源的关闭,打开的文件及网络链接,或者是外部的某个程序开关,增强代码的健张性。

    在有多个catch块的时候,是按照catch块的先后顺序进行匹配的,一旦异常类型被一个catch块匹配,则不会与后面的catch块进行匹配。

       blob.png

      需要注意:在使用try..catch..finally块的时候,注意千万不要在finally块中使用return,因为finally中的return会覆盖已有的返回值。

   

    2. throws和thow关键字

     1)throws出现在方法的声明中,表示该方法可能会抛出的异常,然后交给上层调用它的方法程序处理,允许throws后面跟着多个异常类型;

     2)一般会用于程序出现某种逻辑时程序员主动抛出某种特定类型的异常。throw只会出现在方法体中,当方法在执行过程中遇到异常情况时,将异常信息封装为异常对象,然后throw出去。

  • 异常的使用指南:

     1.只在必要使用异常的地方才使用异常,不要用异常去控制程序的流程

     2.切忌使用空catch块,这意味着忽略异常,相当于异常被吃掉了,在异常排查中很难定位,可以结合Log4J打印异常将该异常进行记录,以便日后方便更新和维护。

     3. 注意catch块的顺序,将不要把上层类的异常放在最前面的catch块。如:将Exception放置case第一位置,那后面的异常都不会执行。

     4.避免多次在日志信息中记录同一个异常:

       只在异常最开始发生的地方进行日志信息记录。很多情况下异常都是层层向上跑出的,如果在每次向上抛出的时候,都Log到日志系统中,则会导致无从查找异常发生的根源。

     5. 异常处理尽量放在最前端的调用者处理异常。

     6. 在finally中释放资源

发表评论