【问题标题】:How to know which variable is the culprit in try block?如何知道哪个变量是 try 块中的罪魁祸首?
【发布时间】:2016-07-28 07:45:57
【问题描述】:

在某个 try 块中,当我使用 Integer.parseInt(string1)Integer.parseInt(string2) 时,我有两个 String 变量可能会导致 NumberFormatException。问题是,如果我catch出现异常,如何知道哪个字符串是麻烦制造者?我需要得到麻烦制造者的变量名。

下面是一些示例代码:

public class test {
    public static void main(String[] args) {
        try {
            String string1 = "fdsa";
            String string2 = "fbbbb";
            Integer.parseInt(string1);
            Integer.parseInt(string2);
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }
        }
    }

e.printStackTrace() 方法并没有告诉我变量名;它只是告诉我麻烦制造者的内容。

java.lang.NumberFormatException:对于输入字符串:“fdsa”在 java.lang.NumberFormatException.forInputString(NumberFormatException.java:65) 在 java.lang.Integer.parseInt(Integer.java:580) 在 java.lang.Integer.parseInt(Integer.java:615) 在 test.main(test.java:9) 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 在 java.lang.reflect.Method.invoke(Method.java:498) 在 com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)

进程以退出代码 0 结束

我需要知道变量名的原因是我需要提示用户发生了什么。例如,告诉用户 string1 是错误的使用

System.out.println(troubleMakerName + "is wrong!")

在我的要求中,用户应该输入

fd=(fileName,maxLength,minLength)

然后我将分析输入字符串并创建一些响应。所以我想检查maxLengthminLength 是否会抛出NumberFormatException。在这种情况下,如果minLength有问题,那么我需要提示用户minLength错误。

【问题讨论】:

  • 1) 您可以查看异常以查看抱怨的值(例如消息、堆栈跟踪中的行号); 2)您可以将parseInts 放在单独的try/catch 块中。
  • e.printStackTrace(),或者任何你的异常变量被调用。但请注意,这不能以编程方式告诉您变量的名称,因为变量名称在运行时不存在。
  • @AndyTurner, 1) 此类中的小代码删除/插入将破坏整个寻线算法。 2)看起来像代码重复
  • @WannabeCoder 如果有一种简单的方法来验证输入,那是正确的。对于整数,至少在标准库中没有。因此,使用 parseInt 并在此处捕获表达式实际上是解决问题的最直接且不易出错的方法。如果您需要的唯一东西是验证整数的能力,那么几乎没有理由包含 Guava 或 apache Commons。不要过度设计。

标签: java exception exception-handling


【解决方案1】:

你有一个XY-Problem

您不想读取实际的变量名称。您希望能够验证输入并向您的用户提供合理的错误消息。

String fileName, maxLengthInput, minLengthInput;
int maxLength, minLength;

List<String> errors = new ArrayList<>();

try {
    maxLength = Integer.parseInt(maxlengthInput);
} catch (NumberFormatException nfe) {
    errors.add("Invalid input for maximum length, input is not a number");
}

try {
    minLength = Integer.parseInt(minlengthInput);
} catch (NumberFormatException nfe) {
    errors.add("Invalid input for minimum length, input is not a number");
}

// show all error strings to the user

不直接抛出异常而是收集它们允许您一次通知用户所有无效输入(可能用红色突出显示相关字段)而不是让他们修复一个输入,尝试再次提交,然后查看另一个输入也是错误的。

您可以使用包含相关字段等信息的自己的数据结构来代替字符串,但这很快就会超出范围。主要思想是:使用两个try-catch块,你可以区分哪个字段是错误的。

如果涉及更多输入,您可以将其重构为循环。

【讨论】:

  • 假设你有 10 个这样的变量需要检查,在这种情况下你会添加 10 个 try-catch 块吗?看起来像代码重复
  • 对于使用 Guava 的人来说,使用 Ints#tryParse 可能会为您节省一些异常性能成本。
  • @AndrewTobilko - 这就是我们也有泛型编程的原因。
  • @AndrewTobilko 不。通常您会从 UI 获得这些输入。这意味着您已经有了一些可以将其全部插入的数据结构。所以我只会遍历我需要的字段,应用必要的验证器(将被抽象掉)并收集有关所有未通过验证的字段的信息并向用户显示正确的信息。但我不会只为两个字段和两个值设计这样的架构。简单有时是最好的。但是,如果您正确阅读了我的答案,我明确表示我会使用涉及更多字段的循环。
  • “给那些使用番石榴的人”
【解决方案2】:

使用 2 个单独的 try,catch 块来解析每个变量的两个输入。然后在每个 catch 块内生成完整性检查消息。

        String string1 = "fdsa";
        String string2 = "fbbbb";
        try {
            Integer.parseInt(string1);
        } catch (NumberFormatException e) {
            e.printStackTrace();
            **//Please provide a valid integer for string1**
        }
        try {
            Integer.parseInt(string2 );
        } catch (NumberFormatException e) {
            e.printStackTrace();
           **//Please provide a valid integer for string2** 
        }

【讨论】:

    【解决方案3】:

    我想写自己的parseInt方法:

    public static int parseInt(String s, 
                               Supplier<? extends RuntimeException> supplier) {
        try {
            return Integer.parseInt(s);
        } catch (NumberFormatException e) {
            throw (RuntimeException)supplier.get().initCause(e);
        }
    }
    

    据我所知,我们无法通过反射读取变量名,因此我只传递了 String 文字:

    parseInt(string1, () -> new NumberFormatException("string1"));
    

    我将留下原始答案并提供在 cmets 中讨论过的版本。但是现在,我很困惑为什么供应商是矫枉过正。

    public static int parseInt(String s, String message) {
        try {
            return Integer.parseInt(s);
        } catch (NumberFormatException e) {
            throw (NumberFormatException)new NumberFormatException(message).initCause(e);
            // throw new IllegalArgumentException(message, e);
        }
    }
    

    它的调用看起来像

    parseInt(string1, "string1");
    

    【讨论】:

    • 你能告诉我() -&gt; new NumberFormatException("string1")是什么吗?
    • @guo,你熟悉 java 8 吗?这是一个返回 Supplier&lt;? extends Exception&gt; 的 lambda 表达式,我们可以在其上调用 get() 来获取异常对象
    • @guo,当然,你可以在没有 java 8 的情况下做到这一点,比如 &lt;E extends Exception&gt; int parseInt(String s, E e) 并且会抛出 e
    • Java8 供应商太过分了,你可以使用parseInt(String maybeNumber, String variableName) 然后throw new IllegalArgumentException(variableName + " is not a number!", e);。这也使堆栈跟踪保持清洁。
    • 这绝对是正确的答案,除了供应商几乎总是会矫枉过正。我将拥有变量名称或错误消息,我将在错误中显示。除此之外,我不敢相信这不能打败复制粘贴的答案。
    【解决方案4】:

    在执行操作之前,您可以编写一个简单的 isInteger() 函数,该函数可以返回一个布尔值。在这个线程上可以找到一个很好的实现。这使用值的基数并在它是 int 时进行迭代并且非常方便。 Determine if a String is an Integer in Java 一个简单的 if 条件 then 可以找到参数中的哪个值是流氓

    【讨论】:

      【解决方案5】:

      对另一个语句使用另一个 try catch

      public class test 
      {
          public static void main(String[] args) 
          {
              String string1 = "fdsa";
              String string2 = "fbbbb";
      
              try 
              {
                  Integer.parseInt(string1);
              } 
              catch (NumberFormatException e) 
              {
                  System.out.println("string1 is the culprit");
                  e.printStackTrace();
              }
      
              try 
              {
                  Integer.parseInt(string2);
              } 
              catch (NumberFormatException e) 
              {
                  System.out.println("string2 is the culprit");
                  e.printStackTrace();
              }
          }
      }
      

      【讨论】:

      • 呃,console.log?我认为你的语言有误。
      • 我正在搜索带有标签“javascript”的问题,然后出现了这个问题,所以我认为这是 javascript。我会解决的。
      • 很公平。 ...与 Java 相比,其余部分看起来真的很像 Javascript 吗?
      • 由于我从未在 javascript 中使用过类,所以我不知道。
      【解决方案6】:

      您可以创建一个带有 try/catch 的方法,该方法返回您分配的值或向控制台打印出现问题的信息,这样您就可以在一个 try/catch 中容纳尽可能多的变量,同时仍然跟踪导致问题的所有变量的名称。

      public class test {
          public static void main(String[] args) {
              String string1 = returnString("fdsa", "string1");
              String string2 = returnString("fbbbb", "string2");
          }
      
          private string returnString(String input, String varName) {
              String str = "";
              try {
                  str = input;
                  Integer.parseInt(str);
              } catch (NumberFormatException e) {
                  System.out.println("Error processing " + varName);
              }
              return str;
          }
      }
      

      【讨论】:

        【解决方案7】:

        我很惊讶没有人提出这样的建议:

        public class test {
        public static void main(String[] args) {
            String errorContext;
        
            try {            
                String string1 = "fdsa";
                String string2 = "fbbbb";
        
                errorContext = "string1";
                Integer.parseInt(string1);
        
                errorContext = "string2";
                Integer.parseInt(string2);
            } catch (NumberFormatException e) {
                System.out.println(errorContext + " is wrong!")
                e.printStackTrace();
            }
            }
        }
        

        这是一个非常简单的解决方案 - 简单,有人会说 - 但它也相当清晰和强大。

        据我所知,这个问题的重点不是提供一种将字符串转换为整数的更好方法(即使它确实可能存在),而是找到一种方法,不仅什么 在很长的try 块中出错了,但在哪里出错了。如果一个人的目标是向用户显示一个有意义的错误消息(最好是建议做什么而不是仅仅抱怨有问题),那么简单地打印堆栈跟踪当然是不够的,并且尝试捕获每一行代码也不是一个有吸引力的选择。

        【讨论】:

        • 这对于一些变量或集合很有用,但如果您有许多单独的变量要检查,则需要为每个变量添加一行代码 - 使用代码大大增加 try 块的大小不会抛出异常。
        【解决方案8】:

        您应该避免捕获Runtime exceptions 并使用其他一些机制来识别错误。在您的情况下,您可以使用匹配器,您的问题可以写成:

        public class test {
        
            private static final Pattern pattern = Pattern.compile("\\d+");
            public static void main(String[] args) {
                    String string1 = "fdsa";
                    String string2 = "fbbbb";
                    if (pattern.matcher(string1).matches()) {
                        Integer.parseInt(string1);
                    } else {
                        //string1 is not an integer
                    }
                    ...
            }
        }
        

        或者你可以写

        boolean errorInLineOne = !pattern.matcher(string1).matches();
        ...
        

        【讨论】:

          【解决方案9】:

          这很简单,但这是我的解决方案..

          public class test
          {
              public static int tryParseInt(String str) throws Exception
              {
                  try
                  {
                      return Integer.parseInt(str);
                  }
                  catch(Exception e)
                  {
                      System.err.println("tryParseInt: couldn't parse '"+str+"'");
                      throw e;
                  }
              }
          
              public static void main(String[] args)
              {
                  try
                  {
                      String string1 = "fdsa";
                      String string2 = "fbbbb";
          
                      tryParseInt(string1);
                      tryParseInt(string2);
                  }
                  catch (NumberFormatException e)
                  {
                      e.printStackTrace();
                  }
              }
          }
          

          【讨论】:

            猜你喜欢
            • 2011-07-19
            • 1970-01-01
            • 2015-04-09
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2012-05-23
            • 1970-01-01
            相关资源
            最近更新 更多