• Java中的异常处理


    异常概述

    Java的基本设计思想是“Badly formed code will not be run!”。这句话的大致意思是:错误形式的代码不会被运行。
    我们在写代码的时候,提升错误恢复能力是提升代码健壮的重要措施。而“为了创建一个更加健壮的系统,那么每一个组件都必须是健壮的”。从而,在Java中出现了异常处理机制。
    不像C语言,基本处理错误的代码都是程序员写上去的,而在Java中,除非是要自己自定义异常的时候,我们一般都是通过异常处理代码块来解决问题的。不但提高了代码的健壮性,还提高了代码的可读性。
    那么,异常处理的定义是什么呢?当程序运行时出现了异常(不是错误),可能是空指针异常等等很多异常,能够对当前出现异常的代码进行处理,或是直接报告异常,或是将异常抛给特定的位置进行决断处理。
    同大多数的需求一样,异常处理也被设计者设计成了一个类:Throwable。在这个类的下面,又有Error(错误)、和Exception(异常)。Error(错误)一般情况下不会通过代码进行处理,因为一般能报错误的情况,都是十分严重的情况,大多数错误都是由JVM(Java虚拟机)引起的。例如下面的代码:

    byte[] buf = new byte[1024*1024*1024];
    System.out.println(buf);

    执行这段代码,肯定是会报错的。原因如下:
    JVM默认情况下只管理了64M的内存,而我们的程序需要的是1G的内存,很显然已经超出了管理范围(内存溢出),所以会报错。
    在Exception(异常)类下,又有RunTimException(运行时异常)以及费运行时异常。这里的“……类下”指的是继承关系。
    下面,我会逐个类介绍,并且会附上相应的代码供大家参考。

    Throwable类

    基本方法
    1. toString() 输出该异常的类名
    2. getMessage() 输出异常的信息,需要通过构造方法传入异常信息,也就是new一个对象的时候传入的参数(手动滑稽)
    3. printStackTrace() 打印栈信息。
    代码片段如下:

    Throwable able = new Throwable("恶心!!!");
    System.out.println(able.toString()); // 输出该异常的类名
    System.out.println(able.getMessage()); // 输出异常的信息
    able.printStackTrace(); // 打印栈信息

    运行结果如下:
    异常使用方式
    那么,我们来看一个出现异常的例子:
    这个函数传入的参数的y值可能是0,程序会出现异常并停止

    public static void div(int x, int y) {
    System.out.println(x / y);
    System.out.println("除法运算");
    }

    那么对于这种情况,我们应该如何进行处理呢?这就正式引入了我们要讨论的话题,异常处理的方式。首先我们来介绍第一种。

    1.try{//可能发生异常的代码 }catch(异常类 变量名){//处理}。

    我们以上面那个除法运算函数作为最基本的例子。当我们没有进行异常处理的时候,程序遇到问题时会停止。进行了异常处理时,程序还会继续执行,并且会按照我们给出的格式进行报错。

    try{
        div(10,0);
    }catch(Throwable t){
            System.out.println(t.toString());
            System.out.println(t.getMessage());
            t.printStackTrace();
            System.out.println();
            System.out.println("除数不能为0");
    }

    程序运行的结果如下图所示:
    这里写图片描述

    那么,一个异常的处理解决了,我们该如何进行多个异常的处理呢?

    多个异常的处理
    为了实现多个异常的处理情况,这里我们使用最简单的方法:设定一个数组。代码如下:

    public class Exception {
        public static void main(String[] args) {
            int[] arr = new int[] {1,2};
            div(1,0,arr);
        }
        public static void div(int i,int j,int arr[]) {
            try {
                System.out.println(arr[1]);
                System.out.println(i / j);
            }catch(ArithmeticException e) {
                e.toString();
                e.getMessage();
                e.printStackTrace();
                System.out.println("******算术异常*******");
            }catch(ArrayIndexOutOfBoundsException e) {
                e.toString();
                e.getMessage();
                e.printStackTrace();
                System.out.println("******数组角标越界*******");
            }catch(NullPointerException e) {
                e.toString();
                e.getMessage();
                e.printStackTrace();
                System.out.println("******空指针异常*******");
            }
        }
    }

    所以,当执行上述代码的时候,将会出现算数异常,也就是ArithmeticException。
    如果将main函数中的代码换成这个样子:

    public void main(String[] args){
        int[] arr = new int[] {1,2};
        arr = null;
        div(1,0,arr);
    }

    这时将会出现空指针异常,也就是NullPointerException ,原因很简单,我们已经将arr数组置为null,所以访问的时候肯定是会出现空指针异常的。
    如果我们再将div中的代码改成下面这个样子:

    System.out.println(arr[3]);

    数组中一共有两个值,0、1,我们访问的是3,很显然是数组角标越界,也就是ArrayIndexOutOfBoundsException 。
    总结
    1. 程序中可能有多个语句发生异常,可以同时放在try中。如果某条语句发生异常的时候,程序将会对catch中的异常进行匹配,如果能够匹配上,则执行相应的catch中的代码,如果没有匹配上,程序停止。
    2. 如果程序中真的出现了多个异常,则只会执行try代码片段中的第一个出现异常的语句的异常处理语句,剩余的异常不会再处理。
    使用多态进行异常处理
    什么是多态呢?我们之前肯定学过,简单来讲,就是“用父类的引用指向子类对象”,我简单解释一下,看下面的代码:

    Father  f = new Son();

    在这里,Son类是继承与Father类的,所以用Father的引用f来指向Son类的对象就是这个意思了,如果还不明白的话,就再好好看看前面的内容。
    具体操作代码如下:

    try {
            System.out.println(arr[1]); // 数组越界
            System.out.println(x / y); // 除零
            Son s = (Son) f; // 类型转换
        } catch (Exception e) {
            e.toString();
            e.getMessage();
            e.printStackTrace();
            System.out.println("出现错误");
        }

    上面的代码很简单,就是说当无论出现什么错误的时候,都只用一个Exception来匹配,我们知道,其实各种各样的异常都是继承于Exception类的,所以可以用Exception的引用指向具体的异常对象,如NullPointerExeception等。
    多个catch语句之间的执行顺序
    1. 按照顺序执行,从上到下
    2. 如果catch的异常有继承关系,则:当子类异常在上,父类异常在下的时候,编译正常。也就是说,如果NullPointerException在上,Exception在下的时候,满足这一条;当子类异常在下,父类异常在上的时候,比如NullPointerException在下,Exception在上,那么就会编译报错,因为子类异常无法catch到。所以,多个异常需要使用子类父类的顺序进行使用。
    注意:处理异常应该catch异常具体的子类,可以处理的更具体,不要为了简化代码使用异常的父类。

    抛出处理

    除了try……catch这种方法进行异常处理外,我们还可以使用抛出处理异常。
    看下面的这个方法:

    public static void div(int x, int y) throws Exception { // 声明异常,通知方法调用者。
        if (y == 0) {
            throw new Exception("除数为0"); // throw关键字后面接受的是具体的异常的对象
        }
        System.out.println(x / y);
        System.out.println("除法运算");
    }

    上面的例子就是抛出处理,使用throw关键字,注意,在方法中使用的是throw,而在方法名上使用的是throws关键字,这个很好理解,有点类似于英文中的复数,一个方法中抛出的异常很有可能不止一个,所以使用throws方法。

    public static void main(String[] args) {
            try {
                div(2, 0);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println("over");
        }

    调用了div方法后,我们需要对这个方法抛出的异常进行处理,这又涉及到了try……catch块。如果不对抛出的异常进行处理,那么编译不会通过。在main函数上依然可以即系抛出,这样就是交给了JVM进行处理了,肯定是不推荐这样做的。
    throw和throws的区别
    1. 相同:都是用于做异常的抛出处理的。
    2. 不同点:
    使用的位置: throws 使用在函数上,throw使用在函数内
    后面接受的内容的个数不同:
    throws 后跟的是异常类,可以跟多个,用逗号隔开。
    throw 后跟异常对象。

    自定义异常

    当现有异常体系中的异常无法满足我们的需求的时候,我们就需要自定义异常。
    根据上面的介绍,我们知道,所有的异常都是继承于父类Exception,所以,我们自定义异常也是继承Exception就可以了。

    class NoRiceException extends Exception {……}

    这个Exception是什么意思呢?
    字面意思就是没有米饭的异常,嘿嘿。
    我们去食堂买饭,就说要买米饭(Rice),然后卖饭的阿姨告诉你,米饭卖没了,这就是NoRiceException,没有米饭的异常,这个异常在现有的异常体系中肯定是没有的。
    下面再来介绍一个大头:

    运行时异常和非运行时异常

    1. RuntimeException(运行时异常)
      RuntimeException的子类有:
      • ClassCastException————–多态中,可以使用Instanceof 判断,进行规避
      • ArithmeticException————–进行if判断,如果除数为0,进行return
      • NullPointerException————-进行if判断,是否为null
      • ArrayIndexOutOfBoundsException————使用数组length属性,避免越界
        这些异常时可以通过程序员的良好编程习惯进行避免的
    2. 非运行时异常(受检异常)
      如果出现了非运行时异常必须进行处理throw或者try{}catch(){}处理,否则编译器报错。
      • IOException 使用要导入包import java.io.IOException;
      • ClassNotFoundException
      • Sun 的API文档中的函数上声明异常,那么该异常是非运行是异常,
        调用者必须处理。
      • 自定义异常一般情况下声明为非运行时异常

    总结
    1. 子类覆盖父类方法时,父类方法抛出异常,子类的覆盖方法可以不抛出异常,或者抛出父类方法的异常,或者该父类方法异常的子类。
    2. 父类方法抛出了多个异常,子类覆盖方法时,只能抛出父类异常的子集
    3. 父类没有抛出异常子类不可抛出异常
    4. 子类发生非运行时异常,需要进行try{}catch的(){}处理,不能抛出。
    5. 子类不能比父类抛出更多的异常

    接下来我们来看异常处理的最后一部分,finally

    finally

    我们知道,当程序出现异常的时候,经过异常处理,程序会停止执行,所以,在处理完异常以后,后续的代码将不会执行,这样肯定是有一定问题的,比如,我们调用了资源,需要关闭这个资源的时候,程序停止了,资源却仍然被占用着,这样是不是很有问题啊。为了处理这样的问题,就要使用finally块。
    finally就是必须执行的代码。
    1. 出现问题的情况:
    try{ // 可能发生异常的代码 } catch( 异常类的类型 e ){ // 当发生指定异常的时候的处理代码 }catch…
    比较适合用于专门的处理异常的代码,不适合释放资源的代码。
    2. 实现方式一:
    try{ } catch(){} finally{ // 释放资源的代码 }
    finally块是程序在正常情况下或异常情况下都会运行的。
    比较适合用于既要处理异常又有资源释放的代码
    3. 实现方式二
    try{ }finally{ // 释放资源 }
    比较适合处理的都是运行时异常且有资源释放的代码。
    4. finally:关键字主要用于释放系统资源。
    - 在处理异常的时候该语句块只能有一个。
    - 无论程序正常还是异常,都执行finally。
    5. finally是否永远都执行?
    - 只有一种情况,但是如果JVM退出了System.exit(0),finally就不执行。
    - return都不能停止finally的执行过程。
    以上,就是异常处理的全部内容。

  • 相关阅读:
    js如何查看元素类型
    selenium之多线程启动grid分布式测试框架封装(四)
    selenium之多线程启动grid分布式测试框架封装(三)
    selenium之多线程启动grid分布式测试框架封装(二)
    selenium之多线程启动grid分布式测试框架封装(一)
    jmeter之自定义java请求性能测试
    jmeter之GUI运行原理
    mongodb操作之使用javaScript实现多表关联查询
    mongodb操作:利用javaScript封装db.collection.find()后可调用函数源码解读
    loadrunner必用函数web_reg_save_param获取多个符合边界值条件的使用方法
  • 原文地址:https://www.cnblogs.com/roobtyan/p/9576733.html
Copyright © 2020-2023  润新知