Java异常及异常处理

Java异常简介

Java异常是Java提供的一种识别及响应错误的一致性机制。
Java异常机制可以使程序中异常处理代码和正常业务代码分离,保证程序代码更加优雅,并提高程序健壮性。按照代码的预先设定的异常处理逻辑,针对性地处理异常,让程序尽最大可能恢复正常并继续执行,且保持代码的清晰。
Java中的异常可以是函数中的语句执行时引发的,也可以是程序员通过throw 语句手动抛出的,只要在Java程序中产生了异常,就会用一个对应类型的异常对象来封装异常,JRE就会试图寻找异常处理程序来处理异常。

Java异常的分类和类结构图

Java标准库内建了一些通用的异常,这些类以Throwable为顶层父类。

Throwable又派生出Error类和Exception类

错误:Error类以及他的子类的实例,代表了JVM本身的错误,比如OutOfMemoryError、ThreadDeath等。错误不能被程序员通过代码处理,Error很少出现。

异常:Exception以及他的子类,代表程序运行时发送的各种不期望发生的事件。可以被Java异常处理机制使用,是异常处理的核心。

Java提供了两类主要的异常:runtimeException和checkedException (检查和非检查是对于javac来说的)

非运行时异常(checkedException):主要是指IO异常、SQL异常等。对于这种异常,JVM要求我们必须对其进行catch处理,所以,面对这种异常,不管我们是否愿意,都是要写一大堆的catch块去处理可能出现的异常。

运行时异常(runtimeException):我们一般不处理,javac在编译时,不会提示和发现这样的异常,不要求在程序处理这些异常。当出现这类异常的时候程序会由虚拟机接管。比如,我们从来没有去处理过NullPointerException、ArithmeticException、ArrayIndexOutOfBoundsException而且这异常还是最常见的异常之一。出现运行时异常的时候,程序会将异常一直向上抛,一直抛到遇到处理代码,如果没有catch块进行处理,到了最上层,如果是多线程就有Thread.run()抛出,如果不是多线程那么就由main.run()抛出。抛出之后,如果是线程,那么该线程也就终止了,如果是主程序,那么该程序也就终止了。

 

异常导图:

Throwable类中的常用方法 :

getCause():返回抛出异常的原因。如果 cause 不存在或未知,则返回 null。

getMessage():返回异常的消息信息。

printStackTrace():对象的堆栈跟踪输出至错误输出流,作为字段 System.err 的值。

Java异常机制用到的几个关键字try、catch、finally、throw、throws。
• try    -- 用于监听。将要被监听的代码(可能抛出异常的代码)放在try语句块之内,当try语句块内发生异常时,异常就被抛出。
• catch   -- 用于捕获异常。catch用来捕获try语句块中发生的异常。
• finally  -- finally语句块总是会被执行。它主要用于回收在try块里打开的物力资源(如数据库连接、网络连接和磁盘文件)。
• throw   -- 用于抛出异常。
• throws   -- 用在方法签名中,用于声明该方法可能抛出的异常。

异常处理的基本语法:

try…catch…finally语句块

try{

     //try块中放可能发生异常的代码。

}catch(SQLException SQLexception){

    //用于捕获可能出现的异常并对异常进行一定的处理,或者这异常类型的子类。

}catch(Exception exception){

    //...

}finally{

   //无论异常是否发生,异常是否匹配被处理,finally都会执行。

   //finally主要做一些清理工作,如流的关闭,数据库连接的关闭等。

}

try、catch、finally三个语句块应注意的问题 :

1、try块中的局部变量和catch块中的局部变量(包括异常变量),以及finally中的局部变量,他们之间不可共享使用。

2、try、catch、finally三个语句块均不能单独使用,三者可以组成 try...catch...finally、try...catch、try...finally三种结构,catch语句可以有一个或多个,finally语句最多一个。

3、try、catch、finally三个代码块中变量的作用域为代码块内部,分别独立而不能相互访问。如果要在三个块中都可以访问,则需要将变量定义到这些块的外面。

4、多个catch块时候,只会匹配其中一个异常类并执行catch块代码,而不会再执行别的catch块,并且匹配catch语句的顺序是由上到下。

通常捕获异常catch的时候最大catch到Exception这个类就为止了,当然这能够处理大部分的异常情况。

但是值得注意的是,Exception不能捕捉到所有的异常。比如InvocationTargetException

解决办法:catch(Throwable t){ }

throws 函数声明

throws声明如果一个方法内部的代码会抛出检查异常(checked exception),而方法自己又没有完全处理掉,则javac保证你必须在方法的签名上使用throws关键字声明这些可能抛出的异常,否则编译不通过。

throws是另一种处理异常的方式,它不同于try…catch…finally,throws仅仅是将函数中可能出现的异常向调用者声明,而自己则不具体处理。

采取这种异常处理的原因可能是:方法本身不知道如何处理这样的异常,或者说让调用者处理更好,调用者需要为可能发生的异常负责。

public void throwsException() throws ExceptionType1 , ExceptionType2 ,ExceptionTypeN

{

  //throwsException内部可以抛出 ExceptionType1 , ExceptionType2 ,ExceptionTypeN 类的异常,或者他们的子类的异常对象。

}

异常的注意事项:

1、当子类重写父类的带有 throws声明的函数时,其throws声明的异常必须在父类异常的可控范围内——用于处理父类的throws方法的异常处理器,必须也适用于子类的这个带throws方法 。这是为了支持多态。

例如,父类方法throws 的是2个异常,子类就不能throws 3个及以上的异常。父类throws IOException,子类就必须throws IOException或者IOException的子类。

2、Java程序可以是多线程的。每一个线程都是一个独立的执行流,独立的函数调用栈。如果程序只有一个线程,那么没有被任何代码处理的异常 会导致程序终止。如果是多线程的,那么没有被任何代码处理的异常仅仅会导致异常所在的线程结束。

也就是说,Java中的异常是线程独立的,线程的问题应该由线程自己来解决,而不要委托到外部,也不会直接影响到其它线程的执行。

常见的ERROR:

Error类包括一些严重的程序不能处理的系统错误类,如内存溢出、虚拟机错误、栈溢出等。这类错误一般与硬件有关,与程序本身无关,通常由系统进行处理,程序本身无法捕获和处理。

  1. OutOfMemoryError内存溢出一般是出现在申请了较多的内存空间没有释放的情形。

    2.StackOverflowError堆栈溢出错误。当一个应用递归调用的层次太深而导致堆栈溢出时抛出该错误。

          

        

 

finally块:

finally块不管异常是否发生,只要对应的try执行了,则它一定也执行。只有一种方法让finally块不执行:System.exit()。因此finally块通常用来做资源释放操作:关闭文件,关闭数据库连接等等。

良好的编程习惯是:在try块中打开资源,在finally块中清理释放这些资源。

   @Test
    public void testfinally() throws Exception {
//        returnCall();
       System.out.println(finallyRet());
    }
    /**
     *在 try块中即便有return,break,continue等改变执行流的语句,finally也会执行。
     * 但是如果抛出了异常未捕获,则不会执行finally。如果有catch,但是未捕获到,则任然会执行 
    finally块。
     * finally中的return 会覆盖 try 或者catch中的返回值。
     * @throws Exception
     */
    private void returnCall() throws Exception {
        try {
//          return 1;
            for (int i = 0; i < 3; i++) {
//                break;
                continue;
            }
            throw new Exception("finally不会执行");
        } finally {
            System.out.print("finally");
            //  return 3;
        }
    }

    /**
     * finally中的return会抑制(消灭)前面try或者catch块中的异常
     * @return
     */
    private int finallyRet() {
        try {
            int a = 666 / 0;
        } catch (Exception e) {
            throw new Exception("我将被忽略,因为下面的finally中使用了return");
        } finally {
            return 6;
        }
    }


    @Test
    public void finallyThrow() throws Exception {
        try{
          int result = 666/0;
        }catch (Exception e){
            throw new Exception("会被覆盖掉");
        }finally {
            throw new Exception("finally中的异常会覆盖(消灭)前面try或者catch中的异常");
        }
    }

 

 

异常进行处理及原则总结

(1)尽量避免出现runtimeException 。例如对于可能出现空指针的代码,带使用对象之前一定要判断一下该对象是否为空,必要的时候对runtimeException也进行try catch处理。

(2)进行try catch处理的时候要在catch代码块中对异常信息进行记录,通过调用异常类的相关方法获取到异常的相关信息,返回到web端,不仅要给用户良好的用户体验,也要能帮助程序员良好的定位异常出现的位置及原因。

(3)能处理就早处理,抛出不去还不能处理的就想法消化掉或者转换为RuntimeException处理。

4)对于一个应用系统来说,应该有自己的一套异常处理框架,这样当异常发生时,也能得到统一的处理风格。

 

猜你喜欢

转载自blog.csdn.net/weixin_37934748/article/details/82598607