在 finally 块中使用 try catch 是否可行?

26

我正在使用缓冲写入器,我的代码在finally块中关闭写入器。我的代码如下。

 ...........
    BufferedWriter theBufferedWriter = null;
    try{
    theBufferedWriter =.....
    ....
    ......
    .....
    } catch (IOException anException) {
    ....
    } finally {
        try {
            theBufferedWriter.close();              
        } catch (IOException anException) {
            anException.printStackTrace();
            }
    }

由于BufferedWriter可能会抛出IOException,因此我必须在finally中的清理代码中使用try-catch。我不想将此异常抛到调用方法中。在finally中使用try-catch是一个好习惯吗?如果不是,有什么替代方案吗?请建议。

谢谢, Hiral


1
先检查是否为空再进行操作是更好的做法。 - qrtt1
+1 这不是很美观,但必须这样做。 - Robben_Ford_Fan_boy
6个回答

15

一种更好的方法是使用Apache commons-io中的IOUtils.closeQuietly。它可以使您的代码更整洁,并消除Java中固有的一些样板文件。

然后,您的代码变成了:

BufferedWriter theBufferedWriter = null;
try{
    theBufferedWriter = ...
    ...
} catch (IOException anException) {
    ...
} finally {
    IOUtils.closeQuietly(theBufferedWriter);
}

更加美观和表达性更强。


3
这样关闭缓冲写入器是有风险的;如果缓冲区中仍有字符,而在尝试写入它们时 close 抛出异常,则数据会被截断,并且您的应用程序无法处理该错误。为了确保安全,您需要在 catch 块之前调用 close 方法。 - McDowell
2
@McDowell:好知道。你应该也可以在 catch 块之前调用 flush(),对吧? - Edward Dale
2
关闭缓冲区将刷新它(请参见Java文档)。如果不清楚,此模式需要调用“close”两次。如果您将“try / finally {close}”移动到现有的“try”块中,则1)只需要调用一次“close”;2)避免冗余的null分配;3)不需要导入第三方库。嵌套的尝试看起来很丑,但通常无法在本地进行错误处理决策,因此catch块将位于更高的调用堆栈中的调用者中。http://illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make-mess-of-stream.html - McDowell

12

在 Java7 之前,我会说你所写的代码是最佳解决方案。

从 Java 7 开始,你可以使用 自动资源管理 功能简化这些操作。使用此功能,你可以执行以下操作:

BufferedWriter theBufferedWriter = null;
try (BufferedWriter theBufferedWriter = ...) {
....
......
.....
} catch (IOException anException) {
....
}

我同意使用资源尝试是自Java 7以来最好的方法:https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html - JavaDev

3

或者你可以使用 Lombok@Cleanup 注解,这样你就再也不用在 finally 内部编写 try-catch 了。

这是通常的编写方式(请注意 throws IOException):

//Vanilly Java

import java.io.*;

public class CleanupExample {
  public static void main(String[] args) throws IOException {
    InputStream in = new FileInputStream(args[0]);
    try {
      OutputStream out = new FileOutputStream(args[1]);
      try {
        byte[] b = new byte[10000];
        while (true) {
          int r = in.read(b);
          if (r == -1) break;
          out.write(b, 0, r);
        }
      } finally {
        out.close();
      }
    } finally {
      in.close();
    }
  }
}

现在使用 Lombok,您只需在流上编写 @Cleanup
import lombok.Cleanup;
import java.io.*;

 public class CleanupExample {
   public static void main(String[] args) throws IOException {
     @Cleanup InputStream in = new FileInputStream(args[0]);
     @Cleanup OutputStream out = new FileOutputStream(args[1]);
     byte[] b = new byte[10000];
     while (true) {
       int r = in.read(b);
       if (r == -1) break;
       out.write(b, 0, r);
     }
   }
 }

1

没问题,但在关闭theBufferedWriter之前,你应该测试它是否为null。
你也可以这样做:

BufferedWriter theBufferedWriter;
try {
    theBufferedWriter = new ...
    try {
        ...
    } finally {
        try {
            theBufferedWriter.close();
        } catch (IOException closeException) {
            closeException.printStackTrace();
        }
    }
} catch (IOException anException) {
    ...
}

或者:

BufferedWriter theBufferedWriter;
try {
    theBufferedWriter = new ...
} catch (IOException createException) {
    // do something with createException 
    return;  // assuming we are in a method returning void
}

try {
    ...
} catch (IOException anException) {
    ...
    // assuming we don't return here
}

try {
    theBufferedWriter.close();
} catch (IOException closeException) {
    closeException.printStackTrace();
}

但是我大多数时候会在专门的方法中执行这些操作(例如写入文件),并且更喜欢抛出异常,以便调用者可以处理它(例如请求另一个文件、停止应用程序等):

void someMethod(...) throws IOException {
    BufferedWriter theBufferedWriter = new ...

    try {
        ...
    } catch (IOExcepption anException) {
        try {
            theBufferedWriter.close();
        } catch (IOException closeException) {
            closeException.printStackTrace();
            // closeException is not thrown, anException represents the main/first problem
        }
        throw anException;
    }

    theBufferedWriter.close();  //  throws the Exception, if any
}

请注意:英语不是我的第一或第二语言,任何帮助将不胜感激


1

这就是我们将不得不忍受的,直到Java 7和ARM Blocks


0

在finally区块中使用try-catch语句是可以的。它是执行你想要做的操作的工具。不过,我认为在关闭(close)时抛出IOException的情况很罕见,因此我允许它压制(body)中的任何异常,就像这样。

try {
    BufferedWriter writer = .....
    try {
        .....
    } finally {
       writer.close();
    }
 } catch (IOException e) {
     ....
 }

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