在预期时处理 NumberFormatException 的正确方法是啥?

Posted

技术标签:

【中文标题】在预期时处理 NumberFormatException 的正确方法是啥?【英文标题】:What is the proper way to handle a NumberFormatException when it is expected?在预期时处理 NumberFormatException 的正确方法是什么? 【发布时间】:2011-05-23 13:05:54 【问题描述】:

我遇到了这种情况,我需要将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() 是否会引发异常。所以我唯一的做法似乎就是调用它并捕获异常。

parseInt 的 javadoc 没有多大帮助。

以下是我想知道的具体问题:

是否有我可以调用的方法来告诉我Integer.parseInt() 是否会在调用之前抛出NumberFormatException?那么我就可以毫无问题地记录它,因为它永远不会发生。 如果我根本没有捕获异常,是否不会分配变量?然后我会简单地将它初始化为我想要的值,当它不是数字并且不捕获异常时。 有没有办法以某种方式明确地标记我不关心的异常?我认为这类似于AWTEvent.consume()。如果是这样,那么我会这样做,以便 Google CodePro 不会将其视为“未记录”。

【问题讨论】:

“如果我根本不捕获异常,变量不会被分配吗?那么我就不会捕获异常。” - 如果您不确定这是否是一个可行的选择,我建议您尝试(并使用调试器进行演练)以 100% 确定您了解在这种情况下会发生什么。我的意思并不是说我在低声说话,但我觉得对异常有深刻的理解很重要。 我不是 Java 程序员,但在 C# 中,Integer 有一个 TryParse() 方法,它尝试解析 int 并返回一个布尔值是否成功。当然,这比期待一个例外要好。 如果有 tryParse() 方法我会喜欢的。我想我理解为什么没有一个,因为它基本上意味着做两次工作。另外,当我尝试它时,未被捕获的NumberFormatException 会当场杀死线程。我更新了问题以反映这一点。 【参考方案1】: 是否有可以调用的方法告诉我 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;
    

【讨论】:

CodePro 不正确地要求记录每次捕获。在这种情况下,吃掉异常是完全可以的,因为这是方法的期望和记录的行为。【参考方案2】:

commons lang 中的 NumberUtils.toInt(String, int) 将完全满足您的需求。

NumberUtils.toInt("123", 42) ==> 123
NumberUtils.toInt("abc", 42) ==> 42

【讨论】:

在不导入 Apache Commons 的情况下我不能以某种方式执行此操作吗? 是的,但是许多项目使用 commons lang 所以不需要推出自己的 impl。 到目前为止我都避免这样做。 NumberUtils 只是抓住了它。抓不住它。 :)【参考方案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

也就是说,不一定需要在每个异常捕获块中包含诊断日志记录。有时,最好的做法是采取默认课程。有时是与用户交互。视情况而定。

【讨论】:

【参考方案4】:

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

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") 注释/关键字。


编辑:我想在下面的 cmets 中指出这些 cmets

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

.

...异常(情况)正在处理,方法是返回指示的 valueIfInvalid。 “糟糕的形式”您指的是盲目和不假思索地编写空 catch 块的不良做法,并且永远不会回头去真正考虑和解决案子。如果考虑了异常情况并为该情况做了正确的事情即使正确的事情是什么也不做),那么你已经“处理”了例外

【讨论】:

这种方法仍然不能处理异常。捕获异常并且不做任何事情是不好的形式。这就是我寻找更好解决方案的原因。 如果你知道你真的不想用它做任何事情,这还不错。忽略应该做一些有用的事情的异常只是一种不好的形式。 @Erick - 我同意@Cameron。通过返回指定的valueIfInvalid 来处理异常(情况)。 “未处理的异常”的一般概念是指盲目和不假思索地编写空 catch 块并且永远不会回头真正考虑和解决该案例的不良做法。如果考虑了异常情况并针对该情况做了正确的事情(即使正确的事情是什么都不做),那么您已经“处理”了异常。 @Bert F-final exception 是什么意思? @Steve Kuo-final 会不会乱成一团【参考方案5】:

您应该在执行过程中捕获异常。这很烦人,但最好的方法。

当字符串不是有效的 int 时,没有任何 Java API 方法会返回 0。

当字符串不是 int 时,将抛出异常,因此您的 int 变量将不会被设置,除非您按照自己的方式捕获异常。

【讨论】:

那我抓到后怎么办?我不想记录它,因为它是正常的。这不是个例。让它不记录工作,但是捕捉异常并且什么都不做是不好的形式。我正在寻找更好的答案。【参考方案6】:

如果不清楚你应该如何从 getter 处理它,你不应该抓住它并让调用者处理它。如果你知道应该如何处理,你就应该这样做。在这种情况下,记录它可能不是必需的或非常有用。

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

【讨论】:

没错。这就是为什么我正在寻找一种方法来将异常标记为已处理而不记录它。【参考方案7】:

您的第一个代码块是正确的。 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;

【讨论】:

如何处理异常以明确将其标记为已处理?捕获异常并且什么都不做是不好的形式,所以我正在寻求更好的解决方案。 不,捕获异常并对其不做任何事情并不是一种糟糕的形式。检查异常的意义在于,您可以捕获它们并按照应用程序的需要处理它们 - 有时涉及日志记录或恢复,但有时您知道这无关紧要。【参考方案8】:

正如其他人所提到的,没有可调用的内置核心 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方法,点击Open Declaration。

【讨论】:

以上是关于在预期时处理 NumberFormatException 的正确方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

多处理池的python保存数据在linux上没有给出预期的结果

MultiResourceItemReader 未按预期工作

刷新状态时出错:S3 中的状态数据没有预期的内容

异常处理

Lumen MySQL 查询未按预期处理 UTF8 值

NumberFormatter 分组未按预期工作