为什么我们需要异常处理?

时间:2014-02-12 12:37:10

标签: syntax exception-handling

我可以检查输入,如果是用户的无效输入,我可以使用简单的“if condition”打印“输入无效,请重新输入”(如果输入无效)。< / p>

这种方法“如果有可能发生故障,使用if条件验证它,然后在遇到故障时指定正确的行为......”对我来说似乎已经足够了。

如果我基本上可以用这种方法覆盖任何类型的失败(除以零等),为什么我需要这整个异常处理机制(异常类和对象,检查和未检查等)?

4 个答案:

答案 0 :(得分:5)

假设您func1通过一些输入调用func2

现在,假设func2由于某种原因失败。

您的建议是在func2内处理失败,然后返回func1

func1如何“知道”func2中发生了什么错误(如果有)以及如何从这一点开始?

首先想到的解决方案是func2将返回的错误代码,通常,零值将表示“OK”,其他每个(非零)值将表示已发生的特定错误。

此机制的问题在于它限制了您添加/处理新错误代码的灵活性。

使用异常机制,您有一个通用的Exception对象,可以扩展到任何特定类型的异常。在某种程度上,它类似于错误代码,但它可以包含更多信息(例如,错误消息字符串)。

当然,你仍然可以争辩,“好吧,那时try/catch是什么?为什么不简单地归还这个对象?”。

幸运的是,这个问题在这里已经得到了非常详细的解答:

In C++ what are the benefits of using exceptions and try / catch instead of just returning an error code?

一般来说,错误代码的异常有两个主要优点,两者都是正确编码的不同方面:

  1. 除了例外,程序员必须处理它或“向上”抛出它,而使用错误代码,程序员可能会错误地忽略它。

  2. 使用异常机制,您可以将代码写得更“干净”,并将所有内容“自动处理”,如果出现错误代码,您必须执行“乏味”switch/case,可能在每个函数“up the call-stack”。

答案 1 :(得分:4)

异常是一种更加面向对象的方法来处理异常执行流而不是返回代码。返回代码的缺点是你必须提出特殊的&#39;值表示不同类型的异常结果,例如:

public double calculatePercentage(int a, int b) {
    if (b == 0) {
        return -1;
    }
    else {      
        return 100.0 * (a / b);
    }
}

上述方法使用返回码-1表示失败(因为它不能除以零)。这可以工作,但是你的调用代码需要知道这个约定,例如这可能发生:

public double addPercentages(int a, int b, int c, int d) {
    double percentage1 = calculatePercentage(a, b);
    double percentage2 = calculatePercentage(c, c);
    return percentage1 + percentage2;
}

上面的代码乍一看看起来很好。但是当b或d为零时,结果将是意外的。 calculatePercentage将返回-1并将其添加到可能不正确的其他百分比。编写addPercentages的程序员在测试之前并不知道此代码中存在错误,即使那时他只是真正检查结果的有效性。

除了例外,你可以这样做:

public double calculatePercentage(int a, int b) {
    if (b == 0) {
        throw new IllegalArgumentException("Second argument cannot be zero");
    }
    else {      
        return 100.0 * (a / b);
    }
}

调用此方法的代码将在没有异常处理的情况下编译,但在使用不正确的值运行时将停止。这通常是首选的方式,因为它将程序员留给处理异常的地方。

如果要强制程序员处理此异常,则应使用已检查的异常,例如:

public double calculatePercentage(int a, int b) throws MyCheckedCalculationException {
    if (b == 0) {
        throw new MyCheckedCalculationException("Second argument cannot be zero");
    }
    else {      
        return 100.0 * (a / b);
    }
}

请注意,calculatePercentage必须在其方法签名中声明异常。必须像这样声明已检查的异常,并且调用代码必须捕获它们或者在它们自己的方法签名中声明它们。

我认为许多Java开发人员目前都认为检查的异常是有点侵入性的,因此大多数API最近倾向于使用未经检查的异常。

上面检查的例外可以这样定义:

public class MyCheckedCalculationException extends Exception {

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

如果您正在开发一个具有多个类和方法的组件,那么创建一个自定义异常类型是有意义的,这些类和方法被其他几个组件使用,并且您希望使API(包括异常处理)非常清晰。

(参见Throwable class hierarchy

答案 2 :(得分:0)

假设您需要为某个对象编写一些代码,该对象由n个不同的资源(n> 3)组成,这些资源将在构造函数中分配并在析构函数内部解除分配。 我们甚至可以说,其中一些资源相互依赖。 例如。为了创建某个文件的内存映射,首先必须成功打开该文件,然后执行OS函数以进行内存映射。 在没有异常处理的情况下,您将无法使用构造函数来分配这些资源,但您可能会使用两步初始化。 你必须自己照顾施工和破坏的顺序 - 因为你不再使用构造函数了。 在没有异常处理的情况下,您将无法将丰富的错误信息返回给调用者 - 这就是为什么在异常免费软件中通常需要调试器和调试可执行文件来识别为什么某些复杂的软件突然失败。 这再次假设,并非每个库都能够将其错误信息转储到stderr。 stderr在某些情况下不可用,这反过来使得使用stderr进行错误报告的所有代码都无法使用。 使用C ++异常处理,您只需将包含匹配系统调用的类链接到基类或成员类关系 AND ,编译器将关注构造和销毁的顺序,并且只调用非失败构造函数的析构函数。

答案 3 :(得分:-1)

首先,方法通常是程序中的代码块或语句块,它使用户能够重用相同的代码,最终节省了过度使用内存。这意味着现在没有浪费计算机上的内存。