Fechando um Java FileInputStream

Tudo bem, tenho feito o seguinte (os nomes das variables ​​foram alterados):

FileInputStream fis = null; try { fis = new FileInputStream(file); ... process ... } catch (IOException e) { ... handle error ... } finally { if (fis != null) fis.close(); } 

Recentemente, comecei a usar o FindBugs, o que sugere que não estou fechando corretamente os streams. Eu decido ver se há algo que pode ser feito com um bloco {} final, e então vejo, oh yeah, close () pode lançar IOException. O que as pessoas deveriam fazer aqui? As bibliotecas Java lançam muitas exceções verificadas.

Para Java 7 e acima, try-with-resources deve ser usado:

 try (InputStream in = new FileInputStream(file)) { // TODO: work } catch (IOException e) { // TODO: handle error } 

Se você está preso no Java 6 ou abaixo …

Esse padrão evita o uso de null :

  try { InputStream in = new FileInputStream(file); try { // TODO: work } finally { in.close(); } } catch (IOException e) { // TODO: error handling } 

Para obter mais detalhes sobre como lidar efetivamente com o fechamento , leia este post: Java: como não fazer bagunça no gerenciamento de stream . Ele tem mais código de amostra, mais profundidade e cobre as armadilhas de fechar o fechamento em um bloco catch .

Algo como o seguinte deve fazê-lo, até você se você jogar ou engolir a IOException na tentativa de fechar o stream.

 FileInputStream fis = null; try { fis = new FileInputStream(file); ... process ... } catch (IOException e) { ... blah blah blah ... } finally { try { if (fis != null) fis.close(); } catch (IOException e) { } } 

Você poderia usar o recurso try-with-resources adicionado JDK7. Foi criado precisamente para lidar com esse tipo de coisa

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

A documenação diz:

A instrução try-with-resources garante que cada recurso seja fechado no final da instrução.

Você também pode usar um método auxiliar simples e estático:

 public static void closeQuietly(InputStream s) { if (null == s) { return; } try { s.close(); } catch (IOException ioe) { //ignore exception } } 

e use isso do seu bloco final.

Nada muito a acrescentar, exceto por uma sugestão estilística muito menor. O exemplo canônico de código de auto-documentação se aplica neste caso – forneça um nome de variável descritivo para a IOException ignorada que você deve capturar em close() .

Então a resposta do squiddle se torna:

 public static void closeQuietly(InputStream s) { try { s.close(); } catch (IOException ignored) { } } 

Na maioria dos casos, acho melhor não pegar as exceções de IO e simplesmente usar try-finally:

 final InputStream is = ... // (assuming some construction that can't return null) try { // process is ... } finally { is.close(); } 

Exceto para FileNotFoundException , você geralmente não pode “contornar” uma IOException . A única coisa que resta a fazer é relatar um erro, e você normalmente vai lidar com isso mais acima na pilha de chamadas, então acho melhor propagar a exceção.

Como IOException é uma exceção verificada, você terá que declarar que esse código (e qualquer um de seus clientes) throws IOException . Isso pode ser muito barulhento ou talvez você não queira revelar os detalhes da implementação do uso de E / S. Nesse caso, você pode encapsular o bloco inteiro com um manipulador de exceção que encapsula a IOException em uma RuntimeException ou um tipo de exceção abstrata.

Detalhe: Estou ciente de que o código acima engole qualquer exceção do bloco try quando a operação de close no bloco finally produz uma exceção IOException . Eu não acho que isso seja um grande problema: geralmente, a exceção do bloco try será a mesma IOException que faz com que o close falhe (ou seja, é muito raro o IO funcionar bem e depois falhar no ponto de fechamento) . Se isso é uma preocupação, pode valer a pena “silenciar” o fechamento.

A solução a seguir lança uma exceção corretamente se o fechamento falhar sem ocultar uma possível exceção antes do fechamento.

 try { InputStream in = new FileInputStream(file); try { // work in.close(); } finally { Closeables.closeQuietly(in); } } catch(IOException exc) { // kernel panic } 

Isso funciona porque chamar perto uma segunda vez não tem efeito .

Isto depende de Closeables de goiaba, mas pode-se escrever seu próprio método closeQuietly se preferir, como mostrado pelo squiddle (veja também serg10 ).

Relatar um erro próximo, no caso geral, é importante porque o close pode gravar alguns bytes finais no stream, por exemplo, por causa do buffer. Portanto, seu usuário quer saber se ele falhou ou você provavelmente quer agir de alguma forma. Concedido, isso pode não ser verdade no caso específico de um FileInputStream, eu não sei (mas por razões já mencionadas, eu acho que é melhor relatar um erro próximo se ocorrer de qualquer maneira).

O código acima é um pouco difícil de entender por causa da estrutura dos blocos try incorporados. Pode ser considerado mais claro com dois methods, um que lança uma IOException e um que a captura. Pelo menos é isso que eu optaria.

 private void work() throws IOException { InputStream in = new FileInputStream(file); try { // work in.close(); } finally { Closeables.closeQuietly(in); } } public void workAndDealWithException() { try { work(); } catch(IOException exc) { // kernel panic } } 

Baseado em http://illegalargumentexception.blogspot.com/2008/10/java-how-not-to-make-mess-of-stream.html (referenciado por McDowell).

Espero que tenhamos fechamentos em Java algum dia, e então perderemos muita verbosidade.

Então, em vez disso, haverá um método auxiliar em javaIO que você pode importar, provavelmente terá uma interface “Closable” e também um bloco. Dentro desse método de ajuda, o try {closable.close ()} catch (IOException ex) {// blah} é definido de uma vez por todas, e então você será capaz de escrever

  Inputstream s = ....; withClosable(s) { //your code here } 

Você está preocupado principalmente em obter um relatório limpo do FindBugs ou com um código que funcione? Estas não são necessariamente a mesma coisa. Seu código original é bom (embora eu iria me livrar da verificação if (fis != null) redundante desde que um OutOfMemoryException teria sido lançado caso contrário). FileInputStream tem um método finalizador que irá fechar o stream para você no caso improvável de você realmente receber uma IOException no seu processamento. Simplesmente não vale o esforço de tornar seu código mais sofisticado para evitar o cenário extremamente improvável de

  1. você recebe uma IOException e
  2. isso acontece com tanta frequência que você começa a encontrar problemas na lista final do finalizador.

Edit: se você está recebendo tantos IOExceptions que você está correndo em problemas com a fila finalizador, então você tem peixes muito maiores para fritar! Trata-se de obter um senso de perspectiva.