Java异常介绍

1. 概述

在计算机中,很多计数都是从0开始的,所以有时候我们得注意一下,像下面代码运行后,就会有问题

public class ExceptionDemo {

    public static void main(String[] args) {
        String[] arr = {"a","b","c"};
        accessArr(arr, 3);
    }

    public static String accessArr(String[] arr ,int index){
        return arr[index];
    }
}

会在控制台中输出下面这些内容

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 3
    at ExceptionDemo.accessArr(ExceptionDemo.java:12)
    at ExceptionDemo.main(ExceptionDemo.java:7)

大家都知道数组下标也是从0开始的,最后一个数组元素的下标是数组长度-1。上面的例子很明显我定义了一个字符串数组并且初始化了3个元素,该数组长度为3,数组最后一个元素的下标应该是2,但是我硬是要访问arr[3],于是就出现了问题,在控制台输出了一些信息。信息中的“ArrayIndexOutOfBoundsException”其实就是一个异常。

那异常到底是什么东西呢?
异常是什么鬼,顾名思义,异常就是不正常的东西。

在Java程序运行中,可能会发生很多不正常的情况。Java作为一门面向对象的编程语言,秉着“万物皆对象”的座右铭,对于这些不正常的情况用类的形式进行了描述和封装对象。所以,描述不正常的情况的类,就称为异常类

不正常的情况会有很多,意味着描述的类也很多,如上面那个ArrayIndexOutOfBoundsException就是一个描述指针访问越界情况的类。将其共性进行向上抽取,并进行分类,就形成了异常体系

2. 异常体系

先贴上一张异常体系图

2.1 Throwable

以下是jdk文档中的描述

Throwable 类是 Java 语言中所有错误或异常的超类。只有当对象是此类(或其子类之一)的实例时,才能通过 Java 虚拟机或者 Java throw 语句抛出。类似地,只有此类或其子类之一才可以是 catch 子句中的参数类型。

要怎么理解它呢?

无论是错误(error),还是异常(exception)都是问题,既然是问题就应该可以抛出去给调用者,让调用者知道并且处理。那么把这个共性抽取出来,不就是可抛性了嘛,而Throwable的翻译也应该是可以
抛,有能力抛的意思。

那可抛性在Java中是怎么体现的呢?

其实是通过throwsthrow这两个关键字来体现的。凡是可以被这两个关键字所操作的类和对象都具备可抛性。

2.2 Error

Error类体系描述了Java运行系统中的内部错误以及资源耗尽的情形.应用程序不应该抛出这种类型的对象,一般是由jvm(虚拟机)抛出。假如出现这种错误,除了尽力使程序安全退出外,在其他方面是无能为力的。这种问题发生一般不针对性处理。直接修改程序

2.3 Exception

以下是jdk文档中的描述

Exception 类及其子类是 Throwable 的一种形式,它指出了合理的应用程序想要捕获的条件。

一句话概括,这类异常是可以处理的。

Exception又有两个分支,一个分支派生于RuntimeException(即运行时异常),另一个分支包含其它异常。划分这两个分支的依据是:由于程序错误导致的异常属于RuntimeException,如访问空指针、数组越界等;而程序本身没有问题,但是由于I/O错误这类问题导致的异常属于其它异常,如试图打开一个不存在的文件,或者反射时根据字符串找不到一个类。所以有句话说的好“如果出现RuntimeException异常,那么就一定是你的问题

2.4 异常类型

异常可以划分成两类

未检查异常(unchecked exception),就是Exception中的RuntimeException和其子类(但是在《Java核心技术》一书中说到派生于Error类的异常也是未检查异常)。这种问题的发生,无法让功能继续,运算无法进行,更多是因为调用者的原因导致的而或者引发了内部状态的改变导致的。那么这种问题一般不处理,直接编译通过,在运行时,让调用者调用时的程序强制停止,让调用者对代码进行修正。如我上面例子的ArrayIndexOutOfBoundsException就是这类异常。

已检查异常(checked exception),只要是Exception和其子类都是,除了特殊子类RuntimeException体系。 这种问题一旦出现,希望在编译时就进行检测,让这种问题有对应的处理方式。这样的问题都可以针对性的处理。如ClassNotFoundException就是这类异常。


该体系有个特点,就是Error的派生类后缀名都是Error,Exception的派生类后缀名都是Exception,阅读性很强。

3. 自定义异常

第一个例子中演示了数组下标越界异常,那么现在问题来了,在我下标是负数的时候,它还是抛出这个异常,我现在想要异常信息更具体点,比如说抛出一个描述下标为负数的异常,然而Java并没有提供这个异常,所以就需要自己定义了。下面就来定义一个描述下标为负数的异常。

要想自定义一个异常,你得先搞清楚它要属于哪种类型的异常,像我要自定义的下标为负数的异常,就应该跟ArrayIndexOutOfBoundsException属于同一类异常,ArrayIndexOutOfBoundsException是属于RuntimeException体系的异常,所以我的下标负数异常就继承RuntimeException,具体代码如下。

public class NegativeIndexException extends RuntimeException{

    public NegativeIndexException() {
        super();
    }

    public NegativeIndexException(String message) {
        super(message);
    }
}

可以看到自定义的异常继承了RuntimeException并提供了两个构造方法,分别在两个构造方法中调用了父类的相应构造方法。这样子一个自定义异常类就定义好了。下面就可以使用一下了,还是用一开始的例子改写一下。

public class ExceptionDemo {

    public static void main(String[] args) {
        String[] arr = { "a", "b", "c" };
        accessArr(arr, -3);
    }

    public static String accessArr(String[] arr, int index) {
        if (index < 0)
            throw new NegativeIndexException("数组下标为负数");

        return arr[index];
    }
}

控制台输出内容

Exception in thread "main" NegativeIndexException: 数组下标为负数
    at ExceptionDemo.accessArr(ExceptionDemo.java:11)
    at ExceptionDemo.main(ExceptionDemo.java:6)

这样子异常信息就可以是自己定义的,更加符合自己的要求。

4. throws 和throw

上面说了凡是可以被throws和throw这两个关键字所操作的类和对象都具备可抛性。那现在就具体介绍一下怎么用。

throws是方法可能抛出异常的声明,意思说该方法可能会抛出某个异常或某些异常,如果你调用它就最好做好处理这些异常的准备。比如说Class类的forName方法就声明了可能会抛出一个ClassNotFoundException。如果是多个异常,则用“,”隔开多个异常。

throw就是抛出一个异常,是异常类的具体对象,告诉调用者异常信息。

throws和throw在用法上的区别

  1. throws使用在函数上。
    throw使用在函数内。
  2. throws抛出的是异常类,可以抛出多个,用逗号隔开。
    throw抛出的是异常对象。

5. try-catch-finally

当我们遇到问题的时候,通常会有两种做法,一种是交给别人处理,一种就是自己解决。在Java中亦是如此,当遇到异常时,可以往上抛给调用者,也可以自己解决。

当我们不能处理的时候,就可以抛给调用者,直接用throw关键字抛出,当我们能处理的时候,就可以用try-catch-finally语句,就是捕获处理异常。

具体格式是:

try{
    //需要被检测异常的代码。
}
catch(异常类 变量){//该变量用于接收发生的异常对象
    //处理异常的代码。
}
finally{
    //一定会被执行的代码。
}

还是用上面的例子,我的main方法调用accessArr方法时如果传入负数就会抛出一个异常,终止了我的程序,那我可以捕获并处理这个异常,像下面这样子。

public class ExceptionDemo {

    public static void main(String[] args) {
        String[] arr = { "a", "b", "c" };
        try {
            accessArr(arr, -3);
        } catch (NegativeIndexException e) {
            System.out.println("知道了知道了,下次不会了");
        }finally {
            System.out.println("这是finally块");
        }
    }

    public static String accessArr(String[] arr, int index) {
        if (index < 0)
            throw new NegativeIndexException("数组下标为负数 ");
        return arr[index];
    }
}

控制台输出内容

知道了知道了,下次不会了
这是finally块

这样子我就可以把这个异常捕获并处理一下了,程序就可以继续往下正常运行直到结束。

其实catch就相当于异常处理器,只要一个异常有一个相应的异常处理器,异常就能够被捕获,进而处理。

而finally块里面的内容,是被称为一定会被执行到的内容,无论有没有出现异常,所以通常把释放资源的代码写在finally块中,如释放数据库连接。其实也并不是那么一定了,如果虚拟机挂了或者程序结束了就执行不到了,比如说调用了System的exit这个方法,该方法就是退出程序的。

6. 异常处理的原则

  1. 函数内容如果抛出已检查的异常,那么函数上必须要声明。否则必须在函数内用try-catch捕获,否则编译失败。
  2. 如果调用到了声明已检查异常的函数,要么try-catch要么throws,否则编译失败。
  3. 什么时候catch,什么时候throws 呢?功能内容可以解决,用catch。解决不了,用throws告诉调用者,由调用者解决 。
  4. 一个功能如果抛出了多个异常,那么调用时,必须有对应多个catch进行针对性的处理。内部又几个需要检测的异常,就抛几个异常,抛出几个,就catch几个。

7. 注意事项

  1. 子类在覆盖父类方法时,父类的方法如果抛出了异常,那么子类的方法只能抛出父类的异常或者该异常的子类。
  2. 如果父类抛出多个异常,那么子类只能抛出父类异常的子集。

简单说:子类覆盖父类只能抛出父类的异常或者子类或者子集。

注意:如果父类的方法没有抛出异常,那么子类覆盖时绝对不能抛,就只能try-catch。


学习异常的语法还远远不够,其中还有很多细节需要注意,以及灵活应用异常机制去提高我们程序的健壮性。


如果上面的内容有错误的地方或者讲的不好的地方,还请大家指点一下,我好及时修改。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值