【问题标题】:try-catch for division by zero尝试捕获除以零
【发布时间】:2010-02-13 21:50:55
【问题描述】:

我的问题是关于简单除零示例中的 try-catch 块。你看到第一行尝试了吗?如果我将这两个变量中的任何一个转换为双精度,程序将无法识别 catch 块。在我看来,无论我是否强制转换,都必须执行 catch 块。这段代码有什么问题?

public static void main(String[] args) {

    int pay=8,payda=0;  

    try {

        double result=pay/(double)payda; // if I cast any of the two variables, program does not recognize the catch block, why is it so?
        System.out.println(result);
        System.out.println("inside-try");

    } catch (Exception e) {

        System.out.println("division by zero exception");
        System.out.println("inside-catch");

    }
}

【问题讨论】:

    标签: java try-catch divide-by-zero


    【解决方案1】:

    除以零对浮点数有效。

    • 1/0 产生无穷大。
    • (-1)/0 产生 -Infinity。
    • 0/0 产生 NaN。

    这些“数字”在 IEEE 754 中得到了正确定义。

    另一方面,整数除以零会抛出异常,因为不能将无穷大表示为 int

    【讨论】:

    • 如果浮点除法不能抛出异常,则应该去掉try/catch,改用错误码。
    • 我认为int 不会抛出,因为它不能代表无穷大。 1/0 根本没有意义,这就是它抛出的原因。另一方面,浮点数挥手说“它应该是无穷大”。
    【解决方案2】:

    我建议验证:

    if (divisor != 0) {
        // calculate
    }
    

    而不是捕获异常

    【讨论】:

    • 除非除数是整数,否则非零除数很容易产生不可用的结果,比如大面额和小除数:“Double div=Double.MAX_VALUE/0.5;”产生无穷大。我建议在检查除数的任何风险计算中检查 Double.isInfinite 和 Double.isNaN(或 Float 等效项)!= 0。
    • 假设它们是整数,如上例所示
    【解决方案3】:

    由于您自称是“新手”,我认为指出您的代码的一些更广泛的问题是个好主意。 (我知道这不是生产代码,但我们只是假设它是。)

    • 如果预期会发生异常,如果检测到会导致异常的条件,通常会更简单、更清晰、更高效。在这种情况下,如果您希望 divisor 偶尔为零,则最好在进行除法之前对其进行测试。

    • 另一方面,如果异常完全出乎意料(即“错误”),最好的策略是让异常传播到最外层。此时,您的应用程序捕获应该捕获所有异常,记录异常堆栈跟踪并退出。

    • 除了上一个项目符号之外,捕捉ExceptionRuntimeExceptionThrowableError 是个坏主意。这样做会捕获大量您可能不打算捕获的异常。在这种情况下,由于您期待的是 ArithmeticException,因此您应该抓住它。

    • 在金融应用程序中使用floatdouble 很少是正确的。这些类型并不准确,但金融应用程序通常需要准确处理大量资金。

    • 更一般地说,浮点数对于非专业人士(和新手)来说本来就很难理解。人们期望持有的许多“真理”实际上并不成立。例如,1.0/3.0 + 1.0/3.0 + 1.0/3.0 的浮点计算不会给出1.0。同样(正如您所发现的)除以零的行为不同。如果你想安全地使用浮点数,你需要了解其中的陷阱。

    编辑详细说明第一点以回应@Jay 的评论。

    首先,我特意使用了“通常”这个词。在某些情况下,避免抛出预期的异常也无济于事。

    话虽如此,您通常不希望发生一般的“预期”异常即使您的应用程序无法立即处理这种情况。例如,如果 OP 的代码是更大的代码的一部分,最好显式抛出 IllegalArgumentException(如果这违反了 API 合同)或检查 UserEnteredRubbishException,如果我们知道这是错误的结果用户输入(并且有一些机会报告/更正它)。

    我们“期待”异常的事实意味着根据定义,我们比(比如说)通用的ArithmeticException 会说的更了解它。如果我们允许一个通用异常传播,它会使一个 catch 块更难处理许多堆栈帧。例如,在这种情况下,远程 catch 块无法确定被零除的原因。 ArithmeticException 可能来自应用程序的不同部分,可能不是甚至被零除!解决的方法是显式抛出更具体的异常。

    【讨论】:

    • 我想对你的第一点提出质疑。如果预期会出现特殊情况,并且您可以采取一些措施来处理它并继续处理,那么是的,您应该检查它而不是抛出异常。但是,如果,无论多么期待一个特殊情况,当它发生时你所能做的就是惊慌失措并放开战犬,那么就抛出一个异常。
    • 但是 #3 和 #4 上的超级同上。捕获一般异常很少有成效。你不知道你在抓什么。顺便说一句,我可能会补充:推论:不要抛出通用异常。如果您的程序检测到错误情况并且您想抛出异常,请花十分钟为它创建一个扩展异常的新类。不要只是 'throw new Exception("Foo was missing a bar")' 之类的。
    【解决方案4】:

    答案在于您的程序自己的输出 - 它为result 打印“Infinity”。这是因为 Java 只不允许整数除以零 - 浮点除零是合法的,并产生 Infinity

    有关 POSITIVE_INFINITYNEGATIVE_INFINITY 常量的信息,请参阅 DoubleFloat 的文档。

    【讨论】:

      【解决方案5】:

      只有用整数除以零才会引发 ArithmeticException。 用 double 或 float 除以零将导致 Infinity

      【讨论】:

        【解决方案6】:

        因为除以两个双精度数返回一个 Infinity,但不会抛出。您可以使用isInfinite() 进行检查。但是,适当的解决方案(如前所述)是在除法之前检查分母。

        【讨论】:

        【解决方案7】:

        没有人应该期望RuntimeException 从他们的代码中被抛出。这些Exceptions 可以(并且应该)很容易地避免。

        【讨论】:

          猜你喜欢
          • 2011-09-01
          • 2021-08-03
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多