【问题标题】:How to surround code with try/catch most elegantly如何最优雅地用 try/catch 包围代码
【发布时间】:2009-06-15 04:42:10
【问题描述】:

我在使用 try-and-catch 时经常遇到一些问题:

1) 某些变量需要在 try 括号内声明,否则它们将不在作用域内
2) 最终,即使我的 return 语句最终也必须在 try 括号中,但该方法不会返回任何内容。

解决此类问题的正确方法是什么。

导致此问题的方法示例如下。它需要处理 FileNotFoundException 和处理 IOException。我怎样才能最优雅地做到这一点?

public static String getContents (File file) {
      BufferedReader reader = new BufferedReader(new FileReader(file));
      String contents = new String();
      while (reader.ready())
        contents += reader.readLine();
      return contents;
    }

【问题讨论】:

    标签: java exception-handling try-catch


    【解决方案1】:

    如果异常处理不需要在getContents 方法中发生,还有一个选择——在方法中添加throws 子句以使方法抛出异常:

    public static String getContents (File file)
        throws IOException, FileNotFoundException {
    

    这样,调用方法的代码将处理Exceptions,而不是方法本身。如果Exceptions 被抛出给调用它的方法,那么该方法中就不需要try/catch 块。

    这可能是也可能不是处理这种情况的理想方式,具体取决于该方法的预期行为方式。

    编辑

    再三考虑,让方法抛出异常可能是个好主意。我认为 D.Shawley 的评论总结得很好——“异常处理应该意味着只处理有意义的异常。”

    在这种情况下,getContents 方法似乎获取了指定File 的内容,并将String 返回给调用者。

    如果要在 getConents 方法中执行异常处理,则传达发生错误的唯一方法是返回某种预先确定的值,例如 null 给调用者以通知调用者发生错误。

    但是,通过让方法本身向调用者抛出异常,调用者可以选择做出相应的反应:

    try {
        String contents = getContents(new File("input.file"));
    } catch (IOException ioe) {
        // Perform exception handling for IOException.
    } catch (FileNotFoundException fnfe) {
        // Inform user that file was not found.
        // Perhaps prompt the user for an alternate file name and try again?
    }
    

    与其让setContents 方法提出自己的协议来通知发生错误,不如将IOExceptionFileNotFoundException 扔回给方法调用者,这样异常处理可以在可以进行适当替代操作的地方执行。

    只有在可以进行一些有意义的处理时才应该执行异常处理。

    【讨论】:

    • +1:异常处理应该意味着只处理有意义的异常。让异常传播应该是恕我直言的默认假设。
    • @D.Shawley 好的,但是在某些时候必须处理异常(除非您提倡最终用户在标准错误中看到漂亮的红色文本)此时,您必须使用 try / catch块是问题的原始点,而不是给出的示例代码。
    • 这仍然有帮助。我一直认为使用 Throws 很糟糕,因为它只是把责任推给了别人。很高兴知道使用 Throws 并不是一个坏习惯。
    【解决方案2】:

    你可以这样处理:

    StringBuilder contents = new StringBuilder();
    BufferedReader reader;
    
    try {
       reader = new BufferedReader(new FileReader(file));
    
       while (reader.ready()) {
          contents.append(reader.readLine());
       }
    
    } catch (FileNotFoundException fne) {
       log.warn("File Not Found", fne);
    } catch (IOException ioe) {
       log.warn("IOException", ioe);
    } 
    
    return contents.toString();
    

    在上述情况下,您可能应该使用 StringBuilder 而不是 String,这在性能方面要好得多。

    【讨论】:

    • 乔恩,你在默默地捕捉异常吗?这可能不是最好的主意,除非这是 Ankur 的意图。不会有错误情况的指示。
    • 抱歉,目的是说明变量的处理,而不是异常的处理。不管怎样,我都修改了...
    • 如果你在 1.5 之前编写了这个,你会使用 StringBuffer 类吗?那么在 1.5 之后哪个代码会表现得更好呢?我自己也不知道,因为我没有对它们中的任何一个进行性能测试。 :) 另外,如果您真的担心性能,那么为什么不直接从 FileReader 读取 char[] 页面,而不是通过 BufferedReader 逐行读取?
    • 请注意,在生产环境中,您需要在记录这些异常后重新抛出这些异常,而不是通过告诉调用方法文件为空来谎言。这可能会导致一连串的次要错误,从而导致灾难性的系统错误(例如,医学数据库事务被提交而不是中止)。
    • @Jordan:StringBuilder 表现更好。 StringBuffer 是线程安全的,因此使用成本更高。 StringBuilder 不是线程安全的,但超过 99% 的时间并不重要。
    【解决方案3】:
    public static String getContents (File file) {
        String contents = new String();
        BufferedReader reader = null;
        try {
            reader = new BufferedReader(new FileReader(file));
            while (reader.ready())
                contents += reader.readLine();
        }
        catch (FileNotFoundException ex) {
            // handle FileNotFoundException
        }
        catch (IOException ex) {
            // handle IOException
        }
        finally {
            if (reader != null) {
                try {
                    reader.close();
                }
                catch (IOException ex) {
                    // handle IOException
                }
            }
        }
        return contents;
    }
    

    我添加了一个finally 块来关闭您的BufferedReader,尽管您没有在代码中这样做。我还建议您使用StringBuilder 而不是String 连接,但有人已经指出了这一点。 reader 的声明和初始化位于 try 块之外,只是因为我添加了 finally 块;否则,引用 reader 可以在 try 块内声明。

    我没有处理异常,我认为这与您的问题无关。

    【讨论】:

    • +1 是第一个提到,读者应该关闭
    【解决方案4】:

    您可以尝试将您的 return 语句移动到 finally 块中。

    【讨论】:

    • 请不要这样做 - finally 块吞下任何抛出和未捕获的异常。
    【解决方案5】:

    关于变量范围,我不确定是否有更优雅的方法来做到这一点。通常我会尝试在出错的情况下考虑我的返回值,然后将变量分配给该值。

    关于return语句,如果你使用我上面的建议,你可以在try / catch块之后返回。

    因此,如果我使用 null 返回值来指示错误,我会这样做

    public static String getContents (File file) {
        String contents = null;
        try {        
            BufferedReader reader = new BufferedReader(new FileReader(file));
            contents = new String();
            while (reader.ready())
                contents += reader.readLine();
        } catch (Exception e) {
            // Error Handling
        }
        return contents;
    }
    

    【讨论】:

      【解决方案6】:

      您在这里缺少的是一个可以大大简化清理工作的简单实用程序:

      public static void closeAndLog(Closable c) {
          if ( c == null )
              return;
      
          try { 
              c.close() 
          } catch ( IOException e) {
              LOGGER.warn("Failed closing " + c +, e);
          }
      }
      

      这样你的代码可以变成:

      public static String getContents (File file) throws IOException {
      
          BufferedReader r = null;
      
          try { 
              r = new BufferedReader(...);
              // do stuff
          } finally {
              closeAndLog(r);
          }
      }
      

      【讨论】:

        【解决方案7】:

        恕我直言,您有两种方法可以正确处理异常(此处为 IOException 和 FileNotFoundException):

        • 你只是扔了它,所以调用者必须处理它,但对方有详细的失败原因,因此可以选择最合适的行为
        • 您只在其中一个中嵌入了两种可能的异常,表示“出了点问题”,因此调用者只有一个异常需要处理(但最终用户消息可能不那么尖锐)

        要获得有关异常的好建议,另请参阅:Why do Java people frequently consume exceptions silently?

        【讨论】:

          【解决方案8】:

          从 Java 7 开始,您可以使用try-with-resources 来确保您的资源正确且“自动”关闭。您只需要一个实现java.lang.AutoCloseable 的对象,BufferedReader 就是这样做的。文档中实际上有以下示例:

          try (BufferedReader br = new BufferedReader(new FileReader(path))) {
              return br.readLine();
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2011-07-19
            • 1970-01-01
            • 1970-01-01
            • 2012-03-29
            • 2016-10-23
            • 2014-08-28
            • 2021-03-17
            • 1970-01-01
            相关资源
            最近更新 更多