当预期出现NumberFormatException时,应该采取什么适当的处理方式?

21

我遇到这样的情况,需要将一个String解析为一个int,但我不知道如何处理NumberFormatException。如果我没有捕获它,编译器不会抱怨,但我想确保我正确处理了这种情况。

private int getCurrentPieceAsInt() {
    int i = 0;
    try {
        i = Integer.parseInt(this.getCurrentPiece());
    } catch (NumberFormatException e) {
        i = 0;
    }
    return i;
}

我想简化我的代码,就像这样。编译器没有问题,但线程在NumberFormatException处停止。

private int getCurrentPieceAsInt() {
    int i = 0;
    i = Integer.parseInt(this.getCurrentPiece());
    return i;
}

Google CodePro希望我以某种方式记录异常,我认为这是最佳实践。

private int getCurrentPieceAsInt() {
    int i = 0;
    try {
        i = Integer.parseInt(this.getCurrentPiece());
    } catch (NumberFormatException e) {
        i = 0;
        e.printStackTrace();
    }
    return i;
}
我希望这个方法在当前对象不是数字或无法解析时返回0。如果我未显式捕获NumberFormatException,那么它会不会分配变量i?或者Integer.parseInt()有一些默认值返回吗?
通常风格规定,如果我捕获了异常,我应该在某个地方记录它。但我不想记录它。这个异常有时被抛出是正常操作,这也让我不太舒服。然而,我找不到一个函数来告诉我是否Integer.parseInt()会抛出一个异常。所以我的唯一选择似乎就是调用它并捕获异常。 parseIntjavadoc没有什么帮助。
以下是我想知道的具体问题:
  • 是否有一种方法可以在调用之前告诉我Integer.parseInt()是否会抛出NumberFormatException?那么我将没有问题记录这个异常,因为它不应该发生。
  • 如果我 simply 不捕获异常,变量会不会被赋值?然后当它不是数字并且没有捕获异常时,我将简单地将其初始化为我想要的值。
  • 是否有一种方式可以明确地标记异常,表明我不在乎它?我在想这会是类似于AWTEvent.consume()的东西。如果是这样,那么我将这样做,以便Google CodePro不会将其视为“未记录”。

如果我不捕获异常,那么变量就不会被赋值吗?那我就不捕获异常。如果你不确定这是否是可行的选项,我建议你尝试一下(并使用调试器进行演示),以确保你完全理解在这种情况下发生了什么。我不是说话有失礼貌,但我认为对异常有扎实的理解很重要。 - Bert F
我不是Java程序员,但在C#中,整数类型拥有TryParse()方法,该方法尝试解析int并返回是否成功的布尔值。这肯定比期望抛出异常要好。 - Ash Burlaczenko
如果有一个 tryParse() 方法的话,我会很喜欢。我想我明白为什么没有这个方法,因为这基本上意味着要做两次工作。此外,当我尝试它时,未捕获的 NumberFormatException 会立即终止线程。我已经更新了问题以反映这一点。 - Erick Robertson
8个回答

14
  • 有没有一种方法可以在调用Integer.parseInt()之前告诉我它是否会抛出NumberFormatException?那么,只要这种情况永远不会发生,我就没有问题记录日志。

遗憾的是,在核心Java API中没有这样的方法。然而,很容易编写一个 - 只需修改下面的代码即可。

  • 如果我简单地不捕获异常,变量不会被赋值吗?那么当它不是数字时,我将简单地将其初始化为所需的值,而不捕获异常。

如果您不捕获异常,那么堆栈将被展开,直到达到处理它的catch块,或者完全展开并停止线程。实际上,该变量不会被赋值,但这不完全符合您的要求。

  • 是否有一种方法可以明确标记异常,表明我不关心它?我想到了类似于AWTEvent.consume()的东西。如果是这样,那么我将这样做,以便Google CodePro不会将其视为“未记录”。

可能有一种方法告诉CodePro忽略此特定警告。当然,使用诸如FindBugs和Checkstyle之类的工具,可以在特定位置关闭警告。(编辑:@Andy已指出如何执行此操作。)

我怀疑您想要的是类似于@daveb提到的Commons lang包中的内容。编写这样一个函数非常容易:

int parseWithDefault(String s, int def) {
    try {
        return Integer.parseInt(s);
    }
    catch (NumberFormatException e) {
        // It's OK to ignore "e" here because returning a default value is the documented behaviour on invalid input.
        return def;
    }
}

4
CodePro 在要求每个 catch 都需要记录日志方面是错误的。在这种情况下,吃掉异常是可以的,因为这是该方法所期望和记录的行为。 - Steve Kuo

10

有没有不导入Apache Commons的情况下可以完成这个操作的方法? - Erick Robertson
是的,但许多项目使用commons lang,因此不需要自己实现。 - daveb
我到目前为止都避免做这两件事。 - Erick Robertson
NumberUtils只是在那里捕获它。不能绕过捕获它。 :) - Joseph Lust

3
* Is there a way to mark the exception somehow explicitly that I don't care about it? I'm thinking this would be something similar to AWTEvent.consume(). If so, then I will do this so that Google CodePro doesn't see this as "unlogged".

是的,您可以在一行代码中本地禁用CodePro审核规则:

http://code.google.com/javadevtools/codepro/doc/features/audit/locally_disabling_audit_rules.html

话虽如此,并非每个异常捕获块中都必须包含诊断日志记录。有时,最好的做法是采取默认操作。有时需要与用户交互。这取决于具体情况。


1

创建自己的便捷方法,以备现在和将来使用:

public static int parseInt(final /*@Nullable*/ String s, final int valueIfInvalid) {
    try {
        if (s == null) {
            return valueIfInvalid;
        } else {
            return Integer.parseInt(s);
        }
    } catch (final NumberFormatException ex) {
        return valueIfInvalid;
    }
}

有没有一种方法可以在调用Integer.parseInt()之前告诉我它是否会抛出NumberFormatException?那么我就没有问题记录这个,因为它不应该发生。
据我所知,没有这样的方法。请记住,如果有这样的方法,您可能最终会解析两次值(一次验证,一次解析)。我理解您想避免异常,但在这种情况下,在Java中捕获异常是标准习语,而且它没有提供其他方法(至少我不知道)。
如果我简单地不捕获异常,变量会被赋值吗?然后当它不是数字时,我将简单地将其初始化为我想要的值,而不捕获异常。
您必须捕获异常(即使什么也不做),否则它将逃逸块并通过堆栈抛出。
有没有一种方法可以明确地标记异常,表明我不关心它?我在想这会是类似于AWTEvent.consume()的东西。如果是这样,那么我将这样做,以便Google CodePro不会将其视为“未记录”。

我不知道有什么替代方法。我会使用上述便捷方法(我在一些通用工具的小集合中有相似的东西,可以在我的所有项目中使用)。

如果您正在处理真正的正常情况,则不必将其记录下来。我不熟悉Google CodePro,但我希望有一种方法可以抑制警告,例如某种@SuppressWarnings("xxx")注释/关键字。


编辑:我想指出下面评论中的这些内容

这种方法仍然无法处理异常。捕获异常并不做任何处理是不好的形式。这就是为什么我正在寻找更好的解决方案。

.

...异常(情况)通过返回指定的IfInvalid值来进行处理。你所提到的“不好的形式”是指盲目地、毫无思考地编写空的catch块,并永远不会回头真正考虑和解决这种情况。如果异常情况得到考虑并为情况做出正确的事情(即使正确的事情是什么都不做),那么你已经“处理”了异常。

这种方法仍然无法处理异常。捕获异常却不做任何处理是不好的形式。这就是为什么我正在寻找更好的解决方案。 - Erick Robertson
2
如果你知道自己真的对它不想做任何事情,那么这并不是什么坏习惯。只有忽略了应该处理的异常才算是坏习惯。 - Cameron Skinner
1
@Erick - 我同意@Cameron的观点。通过返回指定的valueIfInvalid来处理异常(情况)。“未处理的异常”的一般概念是指盲目和不加思考地编写空catch块并永远不会真正考虑和解决问题的不良实践。如果考虑了异常情况并为情况做了正确的事情(即使正确的事情是什么都不做),那么您已经“处理”了异常。 - Bert F
@Bert F- 最终异常是什么意思? - palAlaa
@Alaa - final 关键字 - 这只是我的一个习惯 - 只是意味着 ex 变量将永远不会被赋予另一个值。请参见 https://dev59.com/c3VC5IYBdhLWcg3w9GA9 - Bert F
1
@ Steve Kuo - final永远不会混乱。 - palAlaa

0

正如其他人所提到的,Java核心API中没有内置的方法可以调用来验证整数,但是您可以使用Character类来验证您的输入而不需要使用异常处理。例如:

package com.example.parseint;

public class ValidateIntExample {
    public static boolean isInteger(String s) {
        if (s == null) {
            return false;
        }

        s = s.trim();

        if (s.length() == 0) {
            return false;
        }

        int start = 0;
        if (s.charAt(0) == '-') { // handle negative numbers
            if (s.length() == 1) {
                return false;
            }
            else {
                start = 1;
            }
        }

        for (int i = start; i < s.length(); i++) {
            if (! Character.isDigit(s.charAt(i))) {
                return false;
            }
        }

        return true;
    }
}

实际上,parseInt 本身在内部使用了 Character.isDigit,你可以在 JRE 源代码中验证这一点。(抱歉,我本来想在这里包含 parseInt 方法的,但我不确定是否允许根据许可条款这样做。)如果你正在使用 Eclipse,并且已将 JRE 源代码附加到你的项目中,你可以右键单击你的代码中的 Integer.parseInt 方法,然后点击“打开声明”。

0

您的第一个代码块是正确的。i 不会在异常发生时被隐式转换为0,您必须捕获该异常。在 catch 中将 i 设置为0是正确的; 尽管您可以将 i = 0; 替换为 return 0;。在这种情况下,您无法避免异常处理。

为了澄清,您可以使用以下代码:

private int getCurrentPieceAsInt() {
    int i = 0;
    try {
        i = Integer.parseInt(this.getCurrentPiece());
    } catch (NumberFormatException e) {
        // log that an exception occured if it's needed
        return 0;
    }
    return i;
}

我可以如何处理异常以明确标记它已被处理?捕获异常却不做任何处理是不好的编程习惯,因此我正在寻求更好的解决方案。 - Erick Robertson
2
不,捕获异常并不是一种不好的形式。检查异常的目的是让您可以捕获它们并根据应用程序的需要进行处理 - 有时这涉及日志记录或恢复,但有时您知道这并不重要。 - nojo

0

你应该像现在这样捕获异常。虽然有点烦人,但这是最好的方法。

没有Java API方法可以在字符串不是有效整数时返回0。

当字符串不是整数时,会抛出异常,因此除非你像现在这样捕获异常,否则你的整数变量将不会被设置。


那么我在捕获它之后该怎么办呢?我不想记录它,因为这是正常情况,不是异常情况。不记录下来也可以,但是捕获异常却什么都不做是不好的形式。我正在寻找更好的答案。 - Erick Robertson

0

如果从getter方法中不清楚如何处理异常,那么就不应该捕获它,而是让调用者来处理。如果你知道应该如何处理它,那么就直接处理即可。在这种情况下,记录日志可能并不是必需的或非常有用。

如果你不知道如何处理异常并且将其留给阅读日志的人处理,那么记录异常更有用。


没错。这就是为什么我正在寻找一种方法来标记异常已处理而不记录它的原因。 - Erick Robertson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接