Java的异常处理机制

Java的异常处理机制


简介

           Java的异常结构图

此处输入图片的描述
从图中观察可得:

  1. 所有的异常类是从java.lang.Exception的继承子类。
  2. Exception类是Throwable的子类。Error也是它子类之一,

异常可能在如下几种情景下发生:

  • 用户输入数据不合法。
  • 找不到要打开的文件
  • 网络在传输中断开

程序问题分类标准:

  • 检查型异常 在编译时期出现,也称作为编译时异常,这些异常需要程序员根据提示处理好。
    image_1clqhnb0hb741ch95j5rj9stk37.png-17.3kB
    报错信息:
    image_1clqho3htg7nufjc5n6hf9sp3k.png-23.8kB
  • 非检查型异常 又称作为“运行时异常”,常由 逻辑错误和API的错误调用导致。这类异常在编译时被忽略。
    image_1clqhpufu1fgn113etfuisj1isv41.png-12.1kB
    报错信息:
    image_1clqhq7qk7f7fol1oa0mmq1ot34e.png-16.2kB

PS:我们可以发现,检查型异常在编写时就会有报错信息让你修改,并且编译没有通过。而运行时异常通过了编译,是在运行时报的错。

  • Errors 一般是由运行环境产生的,在编译时期觉察不到,这类问题一般程序员处理不了。
    注:异常可以处理,但是errors是不能处理,也就是说遇到error,程序一般无法自动恢复运行。
    image_1clqi22cn1t50f9d1ml2156u1ps96o.png-14.9kB
    报错信息:
    image_1clqi0v2v1eq21jlk10qa1nl9j9a5b.png-18.6kB
     

关于异常处理:我们一般只捕捉处理编译时异常,而对于运行时异常,因为它们常常是api调用方式不对(参数为空等),我们不会捕捉,而是让程序员发现后进行代码修改,如果强行处理运行时异常,可能会导致逻辑错误更加严重。并且,运行时异常占了绝大多数,处理起来会很麻烦,降低代码阅读性。尽管如此,的确有些特殊情况让我们处理,比如空指针异常。总之,不管你是怎么声明异常,想让你程序运行下去就用检查型异常,想让它停止,就用运行时异常。


常见的异常

1.非检查型异常:
image_1clqhgho119oe1bfup8otks1emo20.png-126.5kB
2.检查型异常:
image_1clqhit1o1c221m9f18osg1m120k2d.png-49.4kB
表格摘自:https://www.runoob.com/java/java-exceptions.html


异常的捕捉方法
1.try catch 语句使用

public static void main(String[] args){
        try{
            int num[] = {1, 2, 3, 4};
            System.out.println(num[5]);
            System.out.println("这是异常后的位置");
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("异常发生,处理异常中---");
        }
        System.out.println("异常处理后---");
    }
输出:
    异常发生,处理异常中--- 
    异常处理后---

注意:try块中发生异常后,异常位置之后的代码不会被执行,异常一旦发生,就会马上去catch中找对应的异常处理方法。

2.多个catch捕捉多个异常 和 一个catch捕捉多个异常

    try {
    } catch (IndexOutOfBoundsException e) {
            System.err.println("IndexOutOfBoundsException: " + e.getMessage());
    } catch (IOException e) {
            System.err.println("Caught IOException: " + e.getMessage());
    }

    //这个时候,里面的引用是隐式的final类型,ex不能改指向。
    catch (IOException|SQLException ex) {
        logger.log(ex);
        throw ex;
    }

3.throws/throw 关键字
二者使用异同?

  • throws写在函数签名末尾,表明了这个函数可能会出现的异常,throw在函数中确切地抛出一个异常。
  • throws后面接异常类名称,throw后面接异常实例
  • throws后面可以接多种异常类名,throw每次只能抛出一个异常。
  • 出现异常后,二者都会抛给上一层,让上一层处理。

代码示例:

throw 使用样例:
public class Example1{  
   void checkAge(int age){  
    if(age<18)  
       throw new ArithmeticException("Not Eligible for voting");  
    else  
       System.out.println("Eligible for voting");  
   }  
   public static void main(String args[]){  
    Example1 obj = new Example1();
    obj.checkAge(13);  
    System.out.println("End Of Program");  
   }  
}

throws 使用样例:
    public void writeList() throws IOException, IndexOutOfBoundsException {}

使用throws的好处:
1.虽然我们可以使用try catch处理异常,但是如果你有多个函数会抛出异常,你得为每个函数都写一个try catch,那样很繁琐,所以我们就将异常统一交给调用外层来处理。
2.当使用throws关键字,你调用该方法编译器会提醒你处理异常,不然报错,这样提升了代码的准确性。

使用总结:
确定会出现异常位置,你可以用throw抛出去。
如果不想自己抛,用throws方法,会帮你把异常抛给函数调用者。

4.finally关键字
从字面上理解,finally是“最后”的意思,在代码中,我们可以理解为“收尾”工作,所谓收尾,就像平时举办活动一样,活动中不管有没有出现异常都会收尾,顺序也肯定是在最后执行。
通常,我们会使用一些streams,connections一些资源接口,在使用完毕后我们要在finally块中收尾显示完成关闭接口。

public static void main(String[] args){
        try{
            int num[] = {1, 2, 3, 4};
            System.out.println(num[5]);
            System.out.println("这是异常后的位置");
        }catch (ArrayIndexOutOfBoundsException e){
            System.out.println("异常发生,处理异常中---");
        }finally {
            System.out.println("异常处理收尾收尾---");
        }
        System.out.println("异常处理后---");
    }
输出:
    异常发生,处理异常中---
    异常处理收尾收尾---
    异常处理后---

一个语法糖try-with-resources
之前我们讲到finally的显示完成关闭接口,在java7之后采用了一种新的方式,可以自动帮我们完成连接的关闭。
方法:
我们只要在try的括号内创建这个reader的对象就行

try(FileReader fr = new FileReader("file path")) {
   // use the resource
   } catch () {
      // body of catch 
   }
}

要使用这个语法糖,必须满足如下几个条件:

  1. 使用的类必须实现了 AutoCloseable 接口;
  2. 你可以在括号里面实例多个资源对象;
  3. 当你实例化了多个对象,关闭时的顺序和声明时的顺序相反
  4. 该resource在try块之前实例化,并且里面的resource引用是隐式的final类型,除此以外并无区别

注意点

  1. try后面必定有catch语句或者finally语句中的一个。
  2. try-catch-finally语句之间不能参杂其他代码,必须是紧跟着。

自定义异常

系统定义的异常太少,不满足你的需求,你也可以自定义异常。但是要注意如下几点:

  1. 所有自定义异常必须间接或者直接是Throwable类的子类。
  2. 如果要写一个检查型异常类,继承Exception类。
  3. 如果写一个运行时异常类,需要继承RuntimeException类。
posted @ 2018-08-27 23:55  顾杰伟  阅读(155)  评论(0编辑  收藏  举报