Java中的try finally块用于关闭流。

36

我想在finally块中关闭我的流,但是它抛出了一个IOException异常,所以似乎我必须在finally块中嵌套另一个try块来关闭流。这是正确的做法吗?这看起来有点笨重。

以下是代码:

 public void read() {
    try {
        r = new BufferedReader(new InputStreamReader(address.openStream()));
        String inLine;
        while ((inLine = r.readLine()) != null) {
            System.out.println(inLine);
        }
    } catch (IOException readException) {
        readException.printStackTrace();
    } finally {
        try {
            if (r!=null) r.close();
        } catch (Exception e){
            e.printStackTrace();
        }
    }


}

可能是嵌套的try/catch块有偏好吗?的重复问题。 - Brian Roach
8个回答

36

另外,如果您使用的是Java 7,您可以使用带资源的try语句

try(BufferedReader r = new BufferedReader(new InputStreamReader(address.openStream()))) {
    String inLine;
    while ((inLine = r.readLine()) != null) {
        System.out.println(inLine);
    }
} catch(IOException readException) {
    readException.printStackTrace();
}           

在堆栈跟踪中打印问题而不记录日志是一个问题,因此我建议人们使用 log.debug(readException.getMessage()); - surendrapanday
1
@suredrapanday 我不认为这应该是一个问题。自12因素以来,记录范例已经改变,所以将日志记录到标准输出不再是一个问题。你可以将所有内容记录到标准输出,就像处理管道一样处理进程,并且可以按照需要处理日志,而无需接触代码。 - Sebastian

28

这似乎有点笨拙。

的确如此。至少在Java7及以上版本中,使用try with resources可以解决这个问题。

在Java7之前,你可以编写一个名为closeStream的函数来阻止这种情况:

public void closeStream(Closeable s){
    try{
        if(s!=null)s.close();
    }catch(IOException e){
        //Log or rethrow as unchecked (like RuntimException) ;)
    }
}

或者将try...finally放在try catch内部:

try{
    BufferedReader r = new BufferedReader(new InputStreamReader(address.openStream()));
    try{

        String inLine;
        while ((inLine = r.readLine()) != null) {
            System.out.println(inLine);
        }
    }finally{
        r.close();
    }
}catch(IOException e){
    e.printStackTrace();
}

它更冗长,在finally中的异常会隐藏try中的异常,但从语义上看更接近于Java 7中引入的try-with-resources


我选择了第二种方法,因为它更加清晰易读。这点非常重要,因为我们希望避免任何内存泄漏。 - Akshay

23

在Java 7中,您可以这样做...

try (BufferedReader r = new BufferedReader(...)){
     String inLine;
     while ((inLine = r.readLine()) != null) {
          System.out.println(inLine);
     }
} catch(IOException e) {
   //handle exception
}
  • 在try块中声明变量需要实现AutoCloseable.
  • 在try块中声明变量也会将其作用域限制在try块中。
  • 在try块中声明的任何变量在try块退出时将自动调用close()方法。

它被称为带资源的try语句


9
是的,它很笨重、丑陋和令人困惑。一个可能的解决方案是使用Commons IO,它提供了一个closeQuietly方法。
在页面右侧的“相关”栏中有许多重复的问题,我建议查看这些内容以寻找处理此问题的其他方法。

6

就像提到 Commons IO 库的答案一样,Google Guava Libraries 也有一个类似的帮助方法,用于处理 java.io.Closeable 类型的对象。该类是 com.google.common.io.Closeables。你要找的函数与 Commons IO 相似:closeQuietly()。

或者你可以自己编写代码来关闭多个对象,例如:Closeables.close(closeable1, closeable2, closeable3, ...) :

import java.io.Closeable;
import java.util.HashMap;
import java.util.Map;

public class Closeables {
  public Map<Closeable, Exception> close(Closeable... closeables) {

  HashMap<Closeable, Exception> exceptions = null;

  for (Closeable closeable : closeables) {
    try {
      if(closeable != null) closeable.close();
    } catch (Exception e) {
        if (exceptions == null) {
          exceptions = new HashMap<Closeable, Exception>();
        }
        exceptions.put(closeable, e);
      }
    }

    return exceptions;
  }
}

即使没有抛出异常,它也会返回任何异常的映射,如果没有异常则返回null。

给那位给我点了踩的人,能否请您解释一下原因,这样我才能从中学习? - Jord Sonneveld
我会给你点赞以平衡一下。Guava是一个很棒的库。 - thaspius

2

你在finally中的做法是正确的。如果在finally块中调用的代码可能会抛出异常,请确保要么处理它,要么记录它。永远不要让它从finally块中冒泡出来。

在catch块中吞掉异常是不正确的。

谢谢...


0
我在你的代码中注意到的第一件事是花括号 { } 缺失。此外,你需要将 r 的值初始化为 null,因此你需要首先传递 null 值给对象,这样你编写的 if 条件可以进行 not null 条件检查,并让你关闭流。

0
public void enumerateBar() throws SQLException {
    Statement statement = null;
    ResultSet resultSet = null;
    Connection connection = getConnection();
    try {
        statement = connection.createStatement();
        resultSet = statement.executeQuery("SELECT * FROM Bar");
        // Use resultSet
    }
    finally {
        try {
            if (resultSet != null)
                resultSet.close();
        }
        finally {
            try {
                if (statement != null)
                    statement.close();
            }
            finally {
                connection.close();
            }
        }
    }
}

private Connection getConnection() {
    return null;
}

来源。 这个示例对我很有用。


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