【java基础】第六章 异常处理

#异常值异常事件,java语言在设计之初就考虑到这些问题,提出异常处理的框架方案,所有的异常都可以用一个类型来表示,不同类型的异常对应不同的子类异常,定义异常处理的规范。在java1.4版本后增加了异常链机制,便于跟踪异常,这是java语言设计者的高明之处,也是java语言中的一个难点,java语言异常处理机制体现了java语言鲁棒性的特点。

#异常(Exception)是程序中可能发生经过处理有可能恢复正常的非正常事件,如IOException(输入输出异常),RuntimeException(运行时异常)。经过处理,不中断程序运行,非致命。

错误(Error),是无法恢复的非正常事件,如OutOfMemoryError(内存溢出错误),ThreadDeath(线程死亡)等。这些错误发生时,java虚拟机一般选择线程终止或退出系统,对程序来说是致命的。

注:只有Throwable及其子类才能被抛出(throw或throws)

#在java中,异常也是对象,从下图知道它继承Throwable及其子类,Throwable中含有一个用于描述异常的字符串。Exception,Error是Throwable的两个最常用子类,而RuntimeException继承自Exception。

异常与错误

注:当内存不足以让java虚拟机分配给一个对象时,将抛出内存不足异常。

#Error及其子类属于系统错误,描述系统内部错误,有jvm抛出,这种错误很少发生,并且程序不能抛出这种类型的异常,一旦发生,除了提醒用户或者终止程序,没有办法处理。所以不用try-catch捕获,编译也可以通过。

Eception及其子类是描述引序或外部环境引起的问题,这些问题可以捕获和处理,他指出了合理的应用程序想要捕获的条件,表示程序本身可以处理的异常。如RuntimeException,是那些可能在java虚拟机正常运行期间抛出的异常的超类,例如空引用异常,数组越界异常,转型用程异常等。

#不受检异常:被认为是可以合理发生的,并不要求捕获处理,如RuntimeException,Error及其子类。反应程序逻辑错误,如没给对象分配空间就访问他(NullPointerException),访问了数组边界以外的位置(IndexOutOfBoundsException)。程序员应该尽量避免这类异常。为了避免try-catch过度使用,java不让程序员捕获这种异常。也就是说java编译器不去检查它,当程序中可能出现这类异常时,即使没有用try-catch,throws语句,还是会编译通过,这种异常可以通过改进代码来避免。

受检异常:不能从运行中合理恢复,编译器强制程序员处理和捕获的异常。如果不处理,程序就不能编译通过。如IO,SQL,及用户自定义异常。一般情况下不要自定义受检异常。受检异常必须显示捕获。

注:不受检异常(RuntimeException):若在函数内抛出,函数上不用声明抛出

若在函数上抛出,调用函数时,不用try-catch处理

下图白色为不受检异常,黑色为受检异常:

异常类的继承层次

#异常处理方法:

一,try-catch-finally

try{
    //尝试运行的程序代码
}catch(异常类型 | 异常的变量名){
    //异常处理代码
}finally{
    //方法返回之前总要执行的代码
}

try中的程序代码要么顺利完成,要么执行到抛出异常。如果抛出异常,就要找出对应于异常类或其父类的catch子句,如果未能找到合适的catch子句,异常就从try语句中抛出去,进入外层可能对他处理的try语句。catch子句可以有多个,但要求捕获的异常不同。如果在try中有finally子句,其代码在try块中所有其他处理完成后执行。无论时正常完成或出现异常,哪怕是通过return或break这样的控制语句结束,finally子句总要执行。在try语句之后至少有一个catch子句或finally子句。

try语句块:表示要尝试运行的代码,try语句块中代码受异常监控,其中代码发生异常会抛出异常对象。

catch语句块:捕获try代码块中发生的异常行为并在代码块中处理。catch语句带有一个Throwable类型的参数,表示可捕获的异常类型。当try语句中出现异常时,catch会捕获到发生的异常对象,与自己参数进行匹配,若匹配,则将catch块参数异常名指向所抛出的异常对象,并执行catch块中的代码。匹配上一个异常后,就不会尝试匹配后面的catch块,因此往往将超类异常参数的catch块放在子类异常参数的catch块的后面。通过异常对象可以获取异常发生时完整的异常堆栈信息,以及异常产生的位置和原因等。

注:catch语句块中一般会在硬盘中创建异常日志文件,便于人们看到处理。

finally语句块:不管try语句块是否发生异常,finally语句块总是在方法返回前执行,目的是给程序一个补救的机会,体现了java语言的健壮性。

注:也可以用于关闭资源(操作数据库出现异常,final可以关闭)

注:三个语句块不能单独使用。必有t,c可有多个,f有则只有一个。

      三个语句块中的变量作用域为语句块内部,分别独立而不能互相访问。

注:子类覆盖父类时,如果父类的方法抛出异常,子类的覆盖方法只能抛出父类的异常或子异常

如果父类方法抛出多个异常,那么子类在覆盖此方法时,只能抛出父类异常的子集。

若子类出现其他异常,只能方法内部自己try-catch处理。

捕获到异常后,通常需要获取异常有关信息,比如异常类型,形质等,Throwable类提供了三个方法获取异常和错误的信息。

①getMessage()获取Throwable对象的详细消息字符串;②toString()方法可以给出Throwable对象的简短描述;3,printStackTrace()将Throwable对象的类型,形质,栈层次及出现在程序中的位置打印出来。

二,把异常对象通过向调用层抛出,一层层向上直至交给java虚拟机处理,称为抛出异常。

java语言允许出现的异常不在当前方法内处理,而是将其抛出,送交到调用它的方法来处理,在调用序列中逐级向上传递,知道传递给java运行时系统,最终找到一个运行层次来处理它。

声明抛出异常是在一个方法声明的throws子句中给出的。语法格式为:

returnType methodName(paramList) throws ExceptionList{。。}

仅当抛出受检异常,该方法的调用者才必须处理或者重新抛出该异常。当方法的调用者无力处理该异常时,应该继续抛出,而不是仅在catch块中用printStackTrace()方法打印一下异常堆栈信息做个勉强处理。

#异常传递链:

将异常发生的原因一个传一个串联起来,沿着调用栈的方向逐层向上抛出。如果在某个方法中产生了异常,该方法会在抛出异常的地方退出;如果不想终止方法,那就需要在特定的区域用try-catch块来捕获异常。如下图所示:

try-catch块处理异常时,如果有异常产生,异常处理机制将从第一个catch块开始匹配异常类型,然后进入catch块进行异常处理操作,这个异常就视为被处理过了。但是如果在方法中无法处理当前异常,可以选择将异常抛到上一层调用该方法处。也就是说如果每个方法都是简单的抛出异常,那么在方法调用方法的多层嵌套调用中,java虚拟机会从出现异常的方法代码块中往回找,知道找到处理该异常的代码块为止。然后将异常交给相应的catch语句处理。当java虚拟机追溯到方法调用最开始的main()方法,但仍未找到处理异常的代码块时,按下面步骤处理。

①调用异常对象的printStackTrace()方法,打印异常栈信息。

②如果出现异常的线程为主线程,则终止整个程序,如果是非主线程,则终止该线程,其他线程继续运行。

由此看出,异常逐层上抛会消耗大量资源,因为要保存一个完整的异常链信息。越早处理异常消耗的资源和事件越小,产生的影响范围夜越小,因此,不要把自己能处理的异常抛出给调用者。

异常处理的一般原则:

①能处理的尽量早处理,抛出去还不能处理的异常要想法处理,或者转换为RuntimeException处理。因为对于一个应用系统来说,抛出大量异常是有问题的,应该从程序开发的角度尽可能地控制异常发生的可能。

②对于受检异常,如果不能很好的处理,可以转换为RuntimeException抛出。这样也让上层代码有选择的余地,可处理也可不处理。

三,对于一个应用系统来说,应该有自己的一套异常处理框架,这样当异常发生时,也能得到统一的处理风格,将准确的异常信息反馈给用户。

#自定义异常

一。自定义异常的创建:

在java类库中现有标准异常类的基础上有用户创建新的异常类,新的异常类必须用extends子句声明为Exception或其子类。例如

public class MyException extends Exception{
        public MyException (String ErrorMessage){
            super(ErrorMessage);
    }
}
//ErrorMessage为自己定义的异常信息 如“出现除负异常” 
//getMessage方法返回的正是这个字符串

自定义异常类需要从已有异常类派生得到,建议新异常从与之最相近的异常类派生,比较简单省事的做法就是让编译器自己定义默认的异常类。

定义异常类的代码写在程序中,与其他类定义并列作为程序的一部分。捕获和处理自定义异常的方式于标准异常类基本相同。

二、抛出异常对象(自定义异常对象)

在方法中使用throw子句可以显式地产生并抛出异常对象,产生并抛出异常对象的语法格式如下:

throw new MyException();

throw关键字用于方法内部,用来抛出一个Throwable类型的对象,如果抛出受检对象,则还应该在方法头部声明方法抛出的异常类型,该方法的调用者也必须捕获处理抛出的受检异常。如果所有方法都层层上抛获取的异常,最终java虚拟机会进行处理,简单地打印异常栈信息。

throw是一种控制程序流程的特殊方法,没有相应的catch,可以终止当前方法继续执行。throws用于对方法进行声明,如果不声明throws,那么一般的Exception都要在这个方法中终结,也就是说一定要有相应的catch处理,否则编译发生错误。如果方法声明了throws,可以交给上一级方法处理,但有些Exception可以不用catch捕获,编译也会通过。通过继承Exception类,可以自定义异常。

#异常转型

捕获到异常后,将异常以新的类型的异常再抛出,这样做一般为了异常的信息更直观。

public void run() throws MyException{
    .....
    try{
        .....
        }catch(IOException e){
            .....
            throw new MyException();
        }finally{
            .....
        }
}

异常转型就是把原始的异常包装为新的异常类,并将新的异常类再次抛出,这样做的目的在于找出产生异常的根本原因。Throwable的来两个构造方法可以创建自定义的包含异常原因的异常类型,也可以通过扩展Exception类来构造带有异常原因的新的异常类。

异常作用:分离正常流程代码和异常处理代码。

当执行到System.exit(0)时,finally不会执行

猜你喜欢

转载自blog.csdn.net/liugf2115/article/details/86358004