corejava11(7.3 使用异常的提示)

7.3 使用异常的提示

关于如何正确使用异常,存在着一定的争议。一些程序员认为所有检查异常都是令人讨厌的,其他人认为似乎扔得不够多。我们认为异常(甚至是检查异常)有它们的用处,并为您提供正确使用它们的提示。

  1. 异常处理不应该替换简单的测试。
    作为一个例子,我们编写了一些代码,尝试10000000次来弹出一个空堆栈。它首先通过找出堆栈是否为空来实现这一点。

    if (!s.empty()) s.pop();
    
    

    接下来,我们强制它弹出堆栈,不管发生什么,然后捕获EmptySackException,它告诉我们不应该这样做。

    try
    {
        s.pop();
    }
    catch (EmptyStackException e)
    {}
    
    

    在我们的测试机器上,调用isEmpty的版本在646毫秒内运行。捕获EmptySackException的版本在21739毫秒内运行。
    正如您所看到的,捕获异常比执行简单测试花费的时间要长得多。寓意是:只在特殊情况下使用例外。

  2. 不要对异常进行微观管理。
    许多程序员将每个语句包装在一个单独的try块中。

    PrintStream out;
    Stack s;
    for (i = 0; i < 100; i++)
    {
        try
        {
            n = s.pop();
        }
        catch (EmptyStackException e)
        {
            // stack was empty
        }
        try
        {
            out.writeInt(n);
        }
        catch (IOException e)
        {
            // problem writing to file
        }
    }
    
    

    这种方法会极大地破坏您的代码。考虑一下您希望代码完成的任务。在这里,我们要从一个堆栈中弹出100个数字,并将它们保存到一个文件中。(别介意为什么它只是一个玩具的例子。)如果一个问题出现在它丑陋的头上,我们就无能为力了。如果堆栈为空,则不会被占用。如果文件包含错误,错误不会神奇地消失。因此,将整个任务包装在一个try块中是有意义的。如果任何一个操作失败,则可以放弃该任务。

    try
    {
        for (i = 0; i < 100; i++)
        {
            n = s.pop();
            out.writeInt(n);
        }
    }
    catch (IOException e)
    {
        // problem writing to file
    }
    catch (EmptyStackException e)
    {
        // stack was empty
    }
    
    

    这个代码看起来更干净。它实现了异常处理的承诺之一:将正常处理与错误处理分开。

  3. 充分利用异常层次结构。
    不要只是抛出RuntimeException。找到合适的子类或创建自己的子类。
    不要只捕获Throwable。它使代码难以阅读和维护。
    尊重检查和不检查异常之间的差异。检查异常本身就很麻烦,不要因为逻辑错误而抛出异常。(例如,反射库出错。调用者通常需要捕获他们知道永远不会发生的异常。)
    不要犹豫,把一个例外变成另一个更合适的例外。例如,在分析文件中的整数时,捕获NumberFormatException并将其转换为IOExceptionMySubSystemException的子类。

  4. 不要压制异常。
    在Java中,让异常闭嘴是一种巨大的诱惑。如果编写的方法调用的方法可能每世纪引发一次异常,编译器会发出呜呜声,因为您没有在方法的throws列表中声明异常。你不想把它放在throws列表中,因为编译器会抱怨所有调用你方法的方法。所以你就让它闭嘴:

    public Image loadImage(String s)
    {
        try
        {
            code that threatens to throw checked exceptions
        }
        catch (Exception e)
        {} // so there
    }
    
    

    现在,您的代码将无障碍地编译。它将正常运行,除非发生异常。然后,异常将被静默地忽略。如果您认为异常非常重要,那么您应该努力正确地处理它们。

  5. 当你发现一个错误,“tough love”比放纵更有效。
    有些程序员担心在检测错误时抛出异常。当使用无效参数调用方法时,返回一个伪值而不是抛出异常可能会更好?例如,Stack.pop应该返回null,还是在堆栈为空时引发异常?我们认为在失败点抛出EmptySackException比在稍后发生NullPointerException要好。

    扫描二维码关注公众号,回复: 6436635 查看本文章
  6. 传播异常并不是羞耻的表现。
    许多程序员感到必须捕获抛出的所有异常。如果调用引发异常的方法,例如FileinputStream构造函数或readLine方法,则会本能地捕获可能生成的异常。通常情况下,最好传播异常而不是捕获它:

    public void readStuff(String filename) throws IOException
    {
        var in = new FileInputStream(filename, StandardCharsets.UTF_8);
        . . .
    }
    
    

    更高级别的方法通常能够更好地通知用户错误或放弃不成功的命令。

注意

规则5和6可以概括为“早抛,晚抓”。

猜你喜欢

转载自blog.csdn.net/nbda1121440/article/details/91496674