• 使用异常的优势


    现在你知道了异常是什么,并且知道怎么使用它们,现在是时候讨论一下在你的程序中使用异常会有什么好处了。
     
    优势1:隔离错误处理代码和常规代码
     
    Exception提供了一种方法,把意外发生时的细节从程序主逻辑中隔离开来。在传统的编程中,错误的检测、报告和处理通常会导致像意大利面条那么混乱的代码。例如,请看下面的伪码,它把整个文件读入内存:
    readFile {
        open the file;
        determine its size;
        allocate that much memory;
        read the file into memory;
        close the file;
    }
    第一眼看过去,这函数简单到不能再简单,但是它忽略了下列所有潜在的问题:
    • 如果文件不能打开怎么办?
    • 如果不能确定文件的大小怎么办?
    • 如果内存不够分配怎么办?
    • 如果文件读取失败怎么办?
    • 如果文件不能关闭怎么办?
     
    为了处理这些问题,readFile函数必须有更多的代码来处理错误检测、报告和处理。这个函数可能会变成这样:
    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;
    }
    有那么多的错误检测、报告和返回语句,让原本简单的5行代码完全迷失在乱哄哄的代码里面了。更糟糕的是,代码的逻辑流程也随着丢失了,因此很难说这段代码是否做了正确的事:当函数分配内存的时候失败了,文件能否确保被关闭?当你三个月之后再修改这段代码时,你就更难确定改完后代码还到底能不能用了。很多程序员解决的办法就是简单地忽略它——当他们的程序崩溃了报告一下错误就行。
     
    Exception可以让你能在一个地方写好代码主流程,在另外一个处理异常情况。如果readFile函数改用Exception来替换传统的错误管理技术,看起来就像下面的代码:
    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;
        }
    }
    要注意的是,使用Exception并不会让你在检测、报告和处理错误时更省力,但是可以让你把这些工作组织起来更高效。
     
    优势2:在调用栈中向上传播错误
     
    Exception的第二个优势就是,可以把错误报告给调用栈的上层方法。假如在主程序中,在一系列嵌套的调用方法中,readFile方法是第四个方法: method1调用method2,然后调用method3,最后调用readFile。
    method1 {
        call method2;
    }
    
    method2 {
        call method3;
    }
    
    method3 {
        call readFile;
    }

    假设method1是唯一关心在readFile内发生了什么错误的方法。传统的错误通知技术强制method1和method2传播readFile返回的错误代码,直到错误代码最后传到method1——而method1是唯一需要返回码的方法。

    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;}

    回忆一下,Java运行时系统通过逆向搜索,查找对某个异常感兴趣的任何方法。一个方法可以躲避任何抛给它的异常,从而让一个方法可以把异常抛到更远的调用栈中捕获。因此,只有关心错误的方法,才必须要去检测错误。

    method1 {
        try {
            call method2;
        } catch (exception e) {
            doErrorProcessing;
        }
    }
    
    method2 throws exception {
        call method3;
    }
    
    method3 throws exception {
        call readFile;
    }
    然而,正如伪代码所示,中间人方法可以躲避掉后面必须要处理的异常。一个方法通过在throws从句中进行声明,可以抛掉任何检查异常。
     
    优势3:归类和区分错误类型
     
    因为程序抛出的所有异常都是对象,类是有层次结构的,所以对异常进行归类或分组是非常自然的结果。在Java平台中有一个例子,一组相关异常被定义在java.io包——IOException及其子类。IOException是最通用的,它代表了我们在执行I/O相关操作的时候发生的任何错误类型。它的继承者代表了更加细分的错误。例如,FileNotFoundException表示一个文件不能再磁盘中定位到。
     
    一个方法可以写一个具体的处理器,能处理一个十分具体的异常。由于FileNotFoundException没有继承者,所以下面的这个异常处理器只能处理一种类型的异常:
    catch (FileNotFoundException e) {
        ...
    }
    一个方法也可以用更通用的处理器捕获处理具体的异常。例如,为了捕获所有的I/O异常,不管具体的类型是什么,只要给异常处理器指定一个IOException参数就行。
    catch (IOException e) {
        ...
    }
    这个处理器可以捕获所有的I/O异常,包括FileNotFoundException,EOFException等等。你可以通过查询传给异常处理器的参数,发现错误发生的细节。例如,用下面的代码打印堆栈跟踪信息:
    catch (IOException e) {
        // Output goes to System.err.
        e.printStackTrace();
        // Send trace to stdout.
        e.printStackTrace(System.out);
    }

    你甚至可以构建一个可以处理所有异常的异常处理器:

    // A (too) general exception handler
    catch (Exception e) {
        ...
    }
    在Throwable类层级结构中,Exception类已经快接近顶点了。所以这个处理器将会捕获很多它不想捕获的异常。如果你想在整个程序中对所有异常都采取统一处理方式,你就可能会采取这种办法处理异常,例如,给用户打印错误信息并退出。
     
    然而在大多数情况下,你希望异常处理器越具体越好。理由是在你决定最佳的恢复策略之前,你首先要知道错误的类型。事实上,如果不捕获具体的错误,这个处理器就必须要容纳任何可能性。太通用的异常处理器可能会让代码更容易出错,因为它们会捕获和处理程序员意料之外的异常,这样就超出处理器的能力范围了。
     
    就像前面说过的,你可以创建一组异常,然后采用通用处理的风格。或者你可以使用具体的异常类型来区分不同的异常,然后用精确的风格处理异常。

     原文地址

    转自:http://leaforbook.com/blog/jexception/translate/advantages.html

  • 相关阅读:
    Python中文乱码(转)
    一千行MySQL学习笔记
    pycharm在同目录下import,pycharm会提示错误,但是可以运行
    PyCharm3.0默认快捷键
    Sublime Text 3 快捷键
    window下spyder的快捷键
    Anaconda更新和第三方包更新
    PyCharm 教程
    centos7.9 源码编译安装php
    centos7.9 源码编译安装nginx
  • 原文地址:https://www.cnblogs.com/hihtml5/p/6505801.html
Copyright © 2020-2023  润新知