抛出了无数的Exception,但是Exception到底是啥?解开Exception的神秘面纱...

java异常



什么是异常呢?

定义:当一个程序在运行过程中,出现了一些非正常执行流程的指令,那么就会产生一个事件对象,这个事件对象在java就简称为异常(Exception)。
An exception is an event, which occurs during the execution of a program, that disrupts the normal flow of the program's instructions.


异常Handler:

当一个异常出现了,那么jvm就会去寻找一个Handler来处理这个异常。
下面树jvm调用过程以及Handler的调用过程,从图中可以看出,Handler过程是反序的。
 





异常的分类:

异常主要分为三类:
Error,代表这个异常非常致命,不需要被异常Handler捕获。
Checked Exception:受检异常,代表异常属于符合用户预期的异常,通常情况下,用户会采取相应的措施,尝试从异常中恢复,需要被异常Handler捕获。
Runtime Exception:运行时异常,通常是一个bug,一般用于表示该异常是用户没有预料到的,不需要被异常Handler捕获。



语法结构:

try语法结构:
try {
    code
}
catch and finally blocks . . .


catch语法结构:
try {


} catch (ExceptionType name) {


} catch (ExceptionType name) {


}


finally语法结构:
try{


}catch(ExceptionType name){


}finally {





直接在方法中抛出并且捕获异常的结构:
public void method1() throws ExceptionType{


}


直接在方法中抛出异常
public void method1(){
    if(index < 0){
        throw new EmptyStackException();
    }
}

异常的作用:

先看一个方法,使用的是伪代码...
readFile {
    open the file;
    determine its size;
    allocate that much memory;
    read the file into memory;
    close the file;
}

在这个方法中,我们会执行如下几个步骤
1打开文件
2查看文件大小
3开辟内存空间
4读取文件内容到内存中
5关闭文件

但是如果这个方法出现如下问题
1文件不可打开
2文件大小不可预知
3内存空间不够了
4如果读取文件失败了
5关闭文件失败了

如果不适用异常来处理这些异常,那么代码会是怎样子呢?
errorCodeType readFile {
    initialize errorCode = 0;
    
    open the file;
    if (theFileIsOpen) {
        determine the length of the file;
        if (gotTheFileLength) {
            allocate that much memory;
            if (gotEnoughMemory) {
                read the file into memory;
                if (readFailed) {
                    errorCode = -1;
                }
            } else {
                errorCode = -2;
            }
        } else {
            errorCode = -3;
        }
        close the file;
        if (theFileDidntClose && errorCode == 0) {
            errorCode = -4;
        } else {
            errorCode = errorCode and -4;
        }
    } else {
        errorCode = -5;
    }
    return errorCode;
}
每次客户端调用这个方法,都需要判断返回的状态码,从而来决定下一步来做什么...比如:
int status = readFile();
switch(status){
case -1: do something...
case -2: do someting...
}


这种代码是会让你发疯的,特别是一段时间之后,你再回来看这些代码,你会发现可读性特别差,如果在readFile()中再添加一些新的状态码,那么客户端的代码需要跟着一起
改变,这会给维护带来毁灭性的的工作,并且很容易引入新的bug。


下面是使用异常之后的代码:

readFile {
    try {
        open the file;
        determine its size;
        allocate that much memory;
        read the file into memory;
        close the file;
    } catch (fileOpenFailed) {
       doSomething;
    } catch (sizeDeterminationFailed) {
        doSomething;
    } catch (memoryAllocationFailed) {
        doSomething;
    } catch (readFailed) {
        doSomething;
    } catch (fileCloseFailed) {
        doSomething;
    }
}

通过异常,我们可以让代码的可读性更加完整,并且如果新增加异常,也不会改动客户端的代码。


我们再来看看另外一个列子

method1 {
    call method2;
}

method2 {
    call method3;
}

method3 {
    call readFile;
}
这是一个方法调用栈,方法method1() call method2() call method()3 call readFile()

如果没用异常处理机制,那么每个方法都会被强制进行错误诊断,那可是相当麻烦的,如下代码:

method1 {
    errorCodeType error;
    error = call method2;
    if (error)
        doErrorProcessing;
    else
        proceed;
}

errorCodeType method2 {
    errorCodeType error;
    error = call method3;
    if (error)
        return error;
    else
        proceed;
}

errorCodeType method3 {
    errorCodeType error;
    error = call readFile;
    if (error)
        return error;
    else
        proceed;
}

好吧,看完之后,你会被很多无关代码弄得心烦意乱了,各种乱...

下面我们使用异常来处理:

method1 {
    try {
        call method2;
    } catch (exception e) {
        doErrorProcessing;
    }
}

method2 throws exception {
    call method3;
}

method3 throws exception {
    call readFile;
}

我们仅仅在只关心的方法中处理异常...瞬间世界美好了。


另外,异常可以帮助我们将一些错误进行分类处理。比如在java.io中,需要各种各种的IO错误,比如打开文件失败,访问文件失败,关闭输出流失败等,我们都可以将这些异常归结为IOException,从更加高的层次去看待问题。


自定义异常:

通常情况下,如果你在开发一个工具包,出现了异常,那么用户可以直接自定义异常。异常的自定义非常简单,通常类名就表示错误类别。如:IllegalArgumentException,IOException,NegativeIndexException,FileNotFoundException等...但是有一点需要注意的是,选择继承的异常类很重要。如果错误是不可预知的,属于bug的,可以继承RuntionException,要么继承Exception吧。



总结:异常给了程序更加高的灵活性,并且让代码更加通熟易懂。所以正确使用异常,是非常重要的。在捕获异常时,应该是做到具体的异常类型,很多人懒惰,就直接在每个方法中throws Exception...可以这会给调试bug带来极大的困难。比如说异常是在一个10个方法堆栈中抛出的,那么异常链就会非常的大,想要定位到错误的所在地是困难的。同样,也不能随意的捕获异常甚至是将异常丢掉了,如下代码try{}catch(Exception){ignore...}。程序明明出现了错误,但是控制台啥也没有输出,这绝对是一个大坑。所以捕获异常的情况一般是:能处理异常,那么就可以捕获异常,如果不行,那么就抛出异常,丢给调用者来决定异常的处理情况。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值