在不丢失原始异常的情况下,在finally块中静默关闭InputStream的正确方法是什么?

时间:2013-04-23 08:01:30

标签: java

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

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

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

9 个答案:

答案 0 :(得分:15)

最好的方法是使用Java 7并使用try with resources,或者手动执行相同的操作,并将关闭中的异常添加为抑制异常。

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

例子: 来自Java tutorials

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也将被关闭。

答案 1 :(得分:7)

Java 6 specs

  

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

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

解决方案可能是如此防御地编写你的finally块,如果finally块失败,那么它是一个更大的意外(值得传播),而不是try catch块出现异常。

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

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

在Java 7中,Alpedar的解决方案当然是可行的。

答案 2 :(得分:6)

您可以使用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);
    }       
}

答案 3 :(得分:2)

is.close()的异常将被禁止,并且is.read()的异常将是传播的异常。

答案 4 :(得分:2)

使用您发布的代码:

  • 如果is.close()抛出IOException,它就会被丢弃,原始异常会传播。
  • 如果is.close()抛出其他内容(RuntimeExceptionError),则会传播该内容并丢弃原始异常。

使用Java 7,关闭InputStream而不丢失原始异常的正确方法是使用try-with-resources statement

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

在Java 7之前,你做的很好,除了你可能想要捕获所有异常,而不仅仅是IOException

答案 5 :(得分:1)

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

请注意,finally块仍将执行,如果Inputstream无效,则会抛出另一个异常,但此异常将被“吞噬”,这可能是可接受的,具体取决于您的用例。

编辑:

根据你问题的标题,我想补充一点,我认为你所拥有的是好的。您可能还需要添加一个catch块来显式处理(或者可能包装)第一个try块中的任何异常,但允许任何IO异常升高也是可以接受的 - 这实际上取决于你的API。 IO异常升高可能会或可能不会被接受。如果是,那么你没关系 - 如果不是那么你可能想用更适合你的程序的东西来处理/包装IO异常。

答案 6 :(得分: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

答案 7 :(得分:0)

  

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

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

答案 8 :(得分: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("----------------");
}
}