Java之异常处理

一.基本概念

异常】:是一个事件,发生在程序执行期间,它中断正在执行程序的正常指令流。异常描述的是由程序和外部环境所引起的错误,这些错误能被程序捕获和处理。

异常对象】:当一个方法发生错误时,将创建一个对象并将它交给运行系统,此对象即为异常对象。该异常对象封装了异常事件的信息。
抛出一个异常】:即创建异常对象并将它交给运行系统。
Java异常处理】:Java程序运行过程中如果出现了异常事件,会生成一个异常对象,该异常对象封装了异常事件的信息并将它提交给正运行的系统,Java运行时系统在接收到异常对象时,会寻找能够处理该异常的代码并将这个异常交给该代码去进行处理。

二.异常产生的原因

 Java 中一个异常的产生,主要有如下三种原因:
  1. Java 内部错误发生异常,Java 虚拟机产生的异常。
  2. 编写的程序代码中的错误所产生的异常,例如空指针异常、数组越界异常等。这种异常称为未检査的异常,一般需要在某些类中集中处理这些异常。
  3. 通过 throw 语句手动生成的异常,这种异常称为检査的异常,一般用来告知该方法的调用者一些必要的信息。

三.异常类

1.Throwable类

相关解释】:
       Java通过API中Throwable类的众多子类描述各种不同的异常。Java异常都是对象,它们是Throwable子类的实例,该对象描述出现在某段编码中的错误信息。【Throwable类】是所有异常类的祖先,其包含两个非常重要的子类:【Error类】和【Exception类】。
Throwable类中的常见方法】:
(1)public String getMessage():获取异常信息,返回字符串。
(2)public String toString:获取异常类名和异常信息,返回字符串。
(3)public void printStackTrace():打印(输出)异常类名和异常信息,以及异常出现在程序中的位置,返回值void。
(4)public void printStackTrace(PrintStream s):通常用该方法将异常内容保存在日志文件中,以便查阅。

2.Error类

相关解释】:
      Error是程序无法处理的错误,由Java虚拟机抛出的,表示正运行的应用程序出现较严重问题。大多数错误与代码编写者执行的操作无关,而表示代码运行Java虚拟机出现的问题。
      这些错误是不可查的,因为它们在应用程序的控制和处理能力以外,而且绝大多数是程序运行时不允许出现的状况。

3.Exception类

相关解释】:
     异常(Exception)描述的是由程序和外部环境所引起的错误,这些错误能被程序捕获和处理。
     Exception类的一个重要子类RuntimeException。RuntimeException类及其子类表示“JVAM常用操作”引发的错误。例如,若试图使用空值对象引用(NullPointerException)、除数为零(ArithmeticException)或数组越界(ArrayIndexOutBoundException),则分别引发运行时异常;

4.异常分类

1.免检异常

相关解释】:
    编译器不要求强制处置的异常。
    例如:RuntimeException、Error以及它们的子类

2.必检异常

相关解释】:
    编译器要求必须处理的异常。
    必检异常虽然是异常状况,但在一定程度上它的发生是可以预计的,而且一旦发生这种异常状况,就必须采取某种方式进行处理。

3.运行时异常

相关解释】:
    运行时异常的特点是Java编译器不会检查它,也就是说,当程序中可能出现这类异常,即使没有用try-catch语句捕获它,也没有用throws子句声明抛出它,也会编译通过。
    程序中可以选择捕获异常,也可以选择不捕获异常,这些错误是由于程序发生逻辑错误而产生的,所以程序应该从逻辑角度尽可能避免这类异常的发生。
常见运行时异常】:


5.处理异常


1.声明异常

相关解释】:
    每个方法都必须声明它可能抛出的必检异常的类型。
    throws语句用在定义方法时声明该方法要抛出的异常类型,如果抛出的是Exception异常类型,则该方法被声明为抛出所有的异常。多个异常可使用逗号分隔。
    throws语句的语法格式:
public class TestException{
    public void test throws Exceptionname1,Exceptionname2,Exceptionname3{
        //方法体
    }
}
声明异常的原则】:
(1)如果是免检异常,即error、RuntimeException或它们的子类,那么可以不使用throws关键字来声明要抛出的异常,其编译依然能顺利通过,但在运行时依旧会被系统抛出。
(2)如果一个方法可能出现必检异常,要么用try-catch语句捕获,要么用throws子句声明将它抛出,否则会导致编译错误。即:必须声明方法可能抛出的任何必检异常。
(3)只有抛出了异常,该方法的调用者才必须去处理或者重新抛出该异常。另外当方法的调用者无法处理该异常,应该继续抛出异常。
(4)调用方法必须遵循任何可查异常的处理和声明规则。
         若覆盖一个方法,则不能声明与覆盖方法不同的异常。声明的任何异常必须是被覆盖方法所声明异常的同类或子类。
void method1() throws IOException{}//合法
//覆盖方法method1
//编译错误,必须捕获或声明抛出IOException
void method2(){
    method1();
}
//合法,声明抛出IOException
void method3()throws IOException{
    method1();
}
//合法,声明抛出Exception,IOException是Exception的子类
void method4()throws Exception{
    method1();
}
//合法,捕获IOException
void method5(){
    try{
        method1();
    }catch(IOException e){...}
}
//编译错误,必须捕获或声明抛出Exception
void method6(){
    try{
        method1();
    }catch(IOException e){throw new Exception();}
}
//合法,声明抛出Exception
void method7()throws Exception{
    try{
        method1();
    }catch(IOException e){throw new Exception();}
}

2.抛出异常

当程序检测一个错误时,程序可以创建一个恰当的异常类型的实例并抛出它。这就被称为抛出一个异常。
throw new TheException("输出异常提示信息");
或
TheException ex = new TheException();
throw ex;
示例

抛出异常注意事项
1.throw总是出现在函数体中,用来抛出一个Throwable类型的异常。
2.程序会在throw语句后立即终止,它后面的语句执行不到,然后在包含它的所有try块中(可能在上层调用函数中)从里向外寻找含有与其匹配的catch子句的try块。
3.throw抛出的只能是可抛出类Throwable或者其子类的实例对象
举例:throw new String("exception");//语句错误,原因String不是可抛出类Throwable或者其子类的实例对象

3.捕获异常

【概念】:
      在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handle)。
寻找过程】:
      运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理其的方法并执行。(最内部调用的方法一遍遍回查到主函数)

示例】:
(1)try代码段包含可能产生异常的代码
(2)try代码段后跟有一个或多个catch代码段
(3)每个catch代码段声明其能处理的一种特定类型的异常 并 提供处理的方法
(4)当异常发生时,程序会中止当前的流程,根据异常获取的类型去执行相应的catch代码段。
(5)finally段的代码无论是否发生异常都要执行
try{
    //可能抛出异常的语句
}catch(SomeException1 e){
    ...
}catch(SomeException2 e){
    ...
}catch(SomeException3 e){
    ...
}finally{
    
}

try语句】:
      try{...}语句指定一段代码,在执行该代码过程中,可能会产生并抛出一种或者多种类型的异常对象,它后面的catch语句会分别对这些异常做出相应的处理。如果未发生异常,try语句后面的所有catch语句将被忽略。
catch语句】:    
       catch中的内容是对异常进行处理的代码。catch语句中声明的异常对象(catch (SomeException e))封装了异常事件发生的信息,在catch语句块中可以使用对这个对象的一些方法获取相关的信息,例如:getMessage()方法和printStackTrace()方法。
finally】语句:
       finally中最常见的工作就是进行资源清理工作,如:
       关闭打开的文件,删除临时文件,释放数据库连接资源.....

四.异常注意事项   

(1)同个try语句后的多个catch语句,子类异常应该在父类异常的前面,否则编译不通过。
举例:NullPointerException异常放在Exception前面  

(2)异常处理通常需要更多的时间和资源。因为异常处理需要初始化新的异常对象,需要从调用栈返回而且还需要沿着方法调用链来传播异常以便找到它的异常处理器。
(3)重写的方法抛出的异常需抛出原方法异常类型一致的异常或者不抛异常。

1.何时抛出异常?
异常出现在方法中。
如果想让该方法的调用者处理异常,那么你应该创建异常对象并将它抛出。如果能在异常的方法中处理异常,那么就不需要抛出异常。
2.什么时候应该使用try-catch块?
当必须处理不可预料的错误时。最好不要用它来处理简单的,可预料的情况。
例如:
//使用try-catch块
try{
    System.out.Println(refVar.toString());
}
catch(NullPointerException ex){
    System.out.Println("refVar is null");
}
//最好使用以下代码代替上面的异常处理
if(refVar != null)
    System.out.Println(refVar.toString());
else
    System.out.Println("refVar is null");
3.自定义异常类
自定义异常类的一般步骤:
     1.通过继承java.lang.Exception来声明自己是异常类
     2.在方法适当的位置生成自定义异常类的实例,并用throw语句抛出
     3.在方法的声明部分用throws语句声明该方法可抛出的异常
自定义异常类示例:

-------------------END-----------------------------