31

我想知道下面的代码是否在 finally 块中正确关闭 InputStream

InputStream is = new FileInputStream("test");
try {
    for(;;) {
        int b = is.read();
        ...
    }
} finally {
    try {
        is.close();
    } catch(IOException e) {
    }
}

如果在 is.read() 期间发生异常,如果在 is.close() 期间发生异常,它会被忽略/抑制吗?

4

9 回答 9

17

最好的方法是使用 Java 7 并使用资源尝试,或者手动执行相同的操作并将关闭时的异常添加为抑制的异常。

Pre Java 7:如果您要抛出自定义异常,则可以像在 Java 7 中那样添加抑制异常(在您的异常中创建字段列表被抑制并放置关闭操作中的异常,在处理您的异常时,看看那里也。如果你不能这样做,我不知道什么比记录它更好。

示例:来自Java 教程

static String readFirstLineFromFile(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}

但更好的形式是:

static String readFirstLineFromFile(String path) throws IOException {
    try (FileReader fr = new FileReader(path);
         BufferedReader br = new BufferedReader(fr)) {
        return br.readLine();
    }
}

这样即使 FileReader 创建成功但 BufferedReader 创建失败(例如内存不足),FileReader 也会被关闭。

于 2013-04-23T08:07:57.593 回答
14

您可以使用来自https://commons.apache.org/proper/commons-io/的 IOUtils 关闭它

public void readStream(InputStream ins) {
    try {
        //do some operation with stream         
    } catch (Exception ex) {
        ex.printStackTrace();
    } finally {
        IOUtils.closeQuietly(ins);
    }
}
于 2015-12-23T07:25:04.787 回答
7

Java 6 规范

如果 try 块的执行由于任何其他原因 R 突然完成,则执行 finally 块。然后有一个选择:如果 finally 块正常完成,那么 try 语句由于原因 R 突然完成。如果 finally 块由于原因 S 突然完成,那么 try 语句由于原因 S 突然完成(并且原因 R 被丢弃)。

所以你是对的,你会失去原来的例外。

解决方案可能是这样防御性地编写您的 finally 块,以至于如果 finally 块失败,而不是 try catch 块中出现异常,这将是一个更大的惊喜(值得传播)。

因此,例如,如果当您尝试关闭流时流可能为空,请检查它:

InputStream is = new FileInputStream("test");
try {
    for(;;) {
        int b = is.read();
        ...
    }
} finally {
    try {
        if( is!=null ) {
            is.close();
        }
    } catch(IOException e) {
    }
}

在 Java 7 中,Alpedar 的解决方案当然是要走的路。

于 2013-04-23T08:50:29.330 回答
3

is.close() 的异常将被抑制,is.read() 的异常将向上传播。

于 2013-04-23T08:04:56.413 回答
2

使用您发布的代码:

  • 如果is.close()抛出一个IOException,它将被丢弃并且原始异常传播。
  • 如果is.close()抛出其他东西(aRuntimeException或 an Error),它会传播并丢弃原始异常。

在 Java 7 中,在不丢失原始异常的情况下关闭 InputStream 的正确方法是使用try-with-resources 语句

try (InputStream is = new FileInputStream("test")) {
    for(;;) {
        int b = is.read();
        // ...
    }
}

在 Java 7 之前,您所做的一切都很好,除了您可能想要捕获所有异常而不是IOExceptions。

于 2013-04-23T09:37:19.670 回答
1

根据您的代码示例,如果int b = is.read();此时发生异常,则异常将在调用链的上层引发。

请注意,尽管 finally 块仍将执行,并且如果Inputstream无效,将引发另一个异常,但此异常将被“吞下”,这可能是可以接受的,具体取决于您的用例。

编辑:

根据您的问题的标题,我想补充一点,我认为您所拥有的一切都很好。您可能希望另外添加一个catch块来显式处理(或包装)第一个try块中的任何异常,但也可以让任何 IO 异常引发 - 这实际上取决于您的 API。让 IO 异常引发可能是可接受的,也可能是不可接受的。如果是,那么你有什么好 - 如果不是,那么你可能想用更适合你的程序的东西来处理/包装 IO 异常。

于 2013-04-23T08:05:32.873 回答
0

下一个解决方案如何:

InputStream is = new FileInputStream("test");
Exception foundException=null;
try {
    for(;;) {
        int b = is.read();
        ...
    }
} catch (Exception e){
  foundException=e;
}
finally {
    if(is!=null)
    try {
        is.close();
    } catch(IOException e) {
    }
}
//handle foundException here if needed
于 2013-04-23T09:48:36.947 回答
0

如果在 is.read() 期间发生异常,如果在 is.close() 期间发生异常,它会被忽略/抑制吗?

是的。您在 close() 中有一个异常的 catch 块,它不会重新引发异常。因此,它不会被传播或重新抛出。

于 2013-04-23T10:56:02.780 回答
0

这是帮助理解您的问题的示例,如果您在 try-catch 块中声明扫描程序,它将向编译器发出警告资源未关闭。所以要么在本地制作,要么在 try() 中制作

import java.util.InputMismatchException;
import java.util.Scanner;

class ScanInt {
public static void main(String[] args) {
    System.out.println("Type an integer in the console: ");

    try (Scanner consoleScanner = new Scanner(System.in);) {
        System.out.println("You typed the integer value: "
                + consoleScanner.nextInt());

    } catch (InputMismatchException | ArrayIndexOutOfBoundsException exception) {
        System.out.println("Catch Bowled");
        exception.printStackTrace();

    }
    System.out.println("----------------");
}
}
于 2014-12-11T17:31:17.510 回答