资讯专栏INFORMATION COLUMN

Java™ 教程(异常的优点)

jollywing / 1455人阅读

异常的优点

现在你已经知道了什么是异常以及如何使用它们,现在是时候了解在程序中使用异常的优势了。

优点1:将错误处理代码与“常规”代码分开

异常提供了从程序的主逻辑中分离异常发生时应该做什么的细节的方法,在传统的编程中,错误检测、报告和处理通常会导致混乱的意大利面代码,例如,考虑这里的伪代码方法将整个文件读入内存。

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

这里有很多错误检测、报告和返回,原始的七行代码在杂乱中丢失了,更糟糕的是,代码的逻辑流程也已丢失,因此很难判断代码是否正在做正确的事情:如果函数无法分配足够的内存,文件是否真的被关闭?在编写方法三个月后修改方法时,确保代码继续做正确的事情变得更加困难,许多程序员通过忽略它来解决这个问题 — 当程序崩溃时会报告错误。

异常使你能够编写代码的主流程,并在其他地方处理异常情况,如果readFile函数使用异常而不是传统的错误管理技术,那么它看起来更像是以下内容。

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

请注意,异常不会使你无需执行检测、报告和处理错误的工作,但它们确实可以帮助你更有效地组织工作。

优点2:在调用堆栈中传播错误

异常的第二个优点是能够在方法的调用堆栈中传播错误报告,假设readFile方法是主程序进行的一系列嵌套方法调用中的第四个方法:method1调用method2,它调用method3,最后调用readFile

method1 {
    call method2;
}

method2 {
    call method3;
}

method3 {
    call readFile;
}

假设method1是唯一对readFile中可能发生的错误感兴趣的方法,传统的错误通知技术强制method2method3readFile返回的错误代码传播到调用堆栈,直到错误代码最终到达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) {
    ...
}

方法可以通过在catch语句中指定任何异常的超类来基于其组或常规类型捕获异常,例如,要捕获所有I/O异常,无论其具体类型如何,异常处理程序都会指定IOException参数。

catch (IOException e) {
    ...
}

此处理程序将能够捕获所有I/O异常,包括FileNotFoundExceptionEOFException等,你可以通过查询传递给异常处理程序的参数来查找有关所发生情况的详细信息,例如,使用以下命令打印堆栈跟踪。

catch (IOException e) {
    // Output goes to System.err.
    e.printStackTrace();
    // Send trace to stdout.
    e.printStackTrace(System.out);
}

你甚至可以设置一个异常处理程序来处理任何Exception

// A (too) general exception handler
catch (Exception e) {
    ...
}

Exception类接近Throwable类层次结构的顶部,因此,除了处理程序要捕获的那些异常之外,此处理程序还将捕获许多其他异常。如果你希望程序执行所有操作,你可能希望以这种方式处理异常,例如,为用户打印出错误消息然后退出。

但是,在大多数情况下,你希望异常处理程序尽可能具体,原因是,处理程序必须做的第一件事是确定发生了什么类型的异常,然后才能决定最佳的恢复策略。实际上,通过不捕获特定错误,处理程序必须适应任何可能性,过于通用的异常处理程序通过捕获和处理程序员没有预料到的异常,以及处理程序没有打算处理的异常,可以使代码更容易出错。

如上所述,你可以创建异常组并以一般方式处理异常,或者你可以使用特定的异常类型来区分异常并以精确的方式处理异常。

总结

程序可以使用异常来指示发生了错误,要抛出异常,请使用throw语句并为其提供异常对象 — Throwable的后代 — 以提供有关发生的特定错误的信息,抛出未捕获的已检查异常的方法必须在其声明中包含throws子句。

程序可以通过结合使用trycatchfinally块来捕获异常。

try块标识可能发生异常的代码块。

catch块标识一个代码块,称为异常处理程序,可以处理特定类型的异常。

finally块标识了一个保证执行的代码块,它是关闭文件、恢复资源以及在try块中包含代码之后进行清理的正确位置。

try语句应包含至少一个catch块或finally块,并且可能有多个catch块。

异常对象的类指示抛出的异常类型,异常对象可以包含有关错误的更多信息,包括错误消息,使用链式异常时,异常可以指向导致异常的异常,异常又可以指向导致它的异常,依此类推。

上一篇:如何抛出异常 下一篇:I/O流

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/77555.html

相关文章

  • Java教程(目录)

    Java™ 教程 Java教程是为JDK 8编写的,本页面中描述的示例和实践没有利用在后续版本中引入的改进。 Java教程是希望使用Java编程语言创建应用程序的程序员的实用指南,其中包括数百个完整的工作示例和数十个课程,相关课程组被组织成教程。 覆盖基础知识的路径 这些教程以书籍的形式提供,如Java教程,第六版,前往Amazon.com购买。 入门 介绍Java技术和安装Java开发软件并使用...

    lifesimple 评论0 收藏0
  • Spring Boot [组件学习-Spring]

    摘要:框架最初是由编写的,并且年月首次在许可下发布。在一个方法执行之后,只有在方法退出抛出异常时,才能执行通知在建议方法调用之前和之后,执行通知。方法执行之后,不考虑其结果,执行通知。 导读: 在上篇文章的结尾提到了Spring Boot 提供了一系列的框架整合(Starter POMs)帮助我们提升开发效率,但是这并不意味着我们不需要学习这些框架,反而更需要去学习,通过学习这些框架可以使...

    raoyi 评论0 收藏0
  • Python入门-Python简介

    摘要:年月宣布支持时间延长到年。更详细的发布列表参阅官网的版本号分为三段,形如。其中表示大版本号,一般当整体重写,或出现不向后兼容的改变时,增加表示功能更新,出现新功能时增加表示小的改动如修复了某个,只要有修改就增加。年公司正式发布。 < 返回索引页 Python语言简介 Python介绍及发展 介绍 Python 官方网站:https://www.python.org/, 大家可以到此处下...

    Java_oldboy 评论0 收藏0
  • 猫头鹰深夜翻译:JAVA异常处理最佳实践

    摘要:无需检查的异常也是的子类。从低层抛出的需检查异常强制要求调用方捕获或是抛出该异常。当前执行的线程将会停止并报告该异常。单元测试允许我在使用中查看异常,并且作为一个可以被执行的文档来使用。不要捕获最高层异常继承的异常同样是的子类。 前言 异常处理的问题之一是知道何时以及如何去使用它。我会讨论一些异常处理的最佳实践,也会总结最近在异常处理上的一些争论。 作为程序员,我们想要写高质量的能够解...

    W_BinaryTree 评论0 收藏0

发表评论

0条评论

jollywing

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<